@djangocfg/nextjs 2.1.412 → 2.1.415
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 +30 -36
- package/dist/config/index.mjs +1 -1
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +110 -137
- package/dist/index.mjs.map +1 -1
- package/dist/sitemap/index.d.mts +126 -56
- package/dist/sitemap/index.mjs +107 -134
- package/dist/sitemap/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/sitemap/README.md +315 -0
- package/src/sitemap/fetch.ts +66 -0
- package/src/sitemap/ids.ts +21 -0
- package/src/sitemap/index.ts +22 -4
- package/src/sitemap/robots.ts +34 -0
- package/src/sitemap/sitemap.ts +94 -0
- package/src/sitemap/types.ts +67 -17
- package/src/sitemap/generator.ts +0 -144
- package/src/sitemap/route.ts +0 -146
package/dist/sitemap/index.d.mts
CHANGED
|
@@ -1,85 +1,155 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MetadataRoute } from 'next';
|
|
2
|
+
import { C as ChangeFreq } from '../types-CwhXnEbK.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Sitemap types
|
|
5
|
+
* Sitemap module — types
|
|
6
|
+
*
|
|
7
|
+
* Wire-contract for `django_cfg.modules.django_sitemap` JSON endpoints
|
|
8
|
+
* plus the public option types for `createDjangoSitemap` and
|
|
9
|
+
* `createRobots`.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
|
-
interface
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
interface SitemapChunkInfo {
|
|
13
|
+
id: string;
|
|
14
|
+
cursor_to: string | null;
|
|
15
|
+
count_estimate: number;
|
|
16
|
+
}
|
|
17
|
+
interface SitemapSourceInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
chunks: SitemapChunkInfo[];
|
|
20
|
+
total_estimate: number;
|
|
21
|
+
}
|
|
22
|
+
interface SitemapIndex {
|
|
23
|
+
sources: SitemapSourceInfo[];
|
|
24
|
+
generated_at: string;
|
|
25
|
+
ttl_seconds: number;
|
|
13
26
|
}
|
|
14
|
-
interface
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
dynamicPages?: (() => Promise<SitemapUrl[]>) | SitemapUrl[];
|
|
18
|
-
cacheControl?: string;
|
|
19
|
-
/** i18n configuration for hreflang support */
|
|
20
|
-
i18n?: SitemapI18nOptions;
|
|
27
|
+
interface SitemapEntry {
|
|
28
|
+
loc: string;
|
|
29
|
+
lastmod?: string | null;
|
|
21
30
|
}
|
|
22
|
-
interface
|
|
31
|
+
interface SitemapFeedPage {
|
|
32
|
+
source: string;
|
|
33
|
+
chunk_id: string;
|
|
34
|
+
count: number;
|
|
35
|
+
has_more: boolean;
|
|
36
|
+
next_cursor: string | null;
|
|
37
|
+
entries: SitemapEntry[];
|
|
38
|
+
}
|
|
39
|
+
interface StaticRoute {
|
|
23
40
|
path: string;
|
|
24
|
-
|
|
25
|
-
changefreq?: ChangeFreq;
|
|
41
|
+
changeFrequency?: ChangeFreq;
|
|
26
42
|
priority?: number;
|
|
27
43
|
}
|
|
44
|
+
interface CreateDjangoSitemapOptions {
|
|
45
|
+
/** Canonical site host, e.g. `https://vamcar.com`. URLs in the sitemap
|
|
46
|
+
* are joined as `${host}${loc}`. */
|
|
47
|
+
host: string;
|
|
48
|
+
/** Django backend base URL. When omitted, backend chunks are skipped
|
|
49
|
+
* and only `staticRoutes` are emitted (single-chunk sitemap). */
|
|
50
|
+
apiUrl?: string;
|
|
51
|
+
/** Frontend-only routes the backend doesn't know about. Auth/account
|
|
52
|
+
* pages should NOT be listed here. */
|
|
53
|
+
staticRoutes?: StaticRoute[];
|
|
54
|
+
/** Index revalidate window in seconds. Default: 600. */
|
|
55
|
+
indexRevalidate?: number;
|
|
56
|
+
/** Feed (chunk) revalidate window in seconds. Default: 3600. */
|
|
57
|
+
feedRevalidate?: number;
|
|
58
|
+
}
|
|
59
|
+
interface CreateRobotsOptions {
|
|
60
|
+
host: string;
|
|
61
|
+
/** Disallow patterns. Default: ['/account/', '/auth', '/api/']. */
|
|
62
|
+
disallow?: string[];
|
|
63
|
+
/** Override sitemap URL. Default: `${host}/sitemap.xml`. */
|
|
64
|
+
sitemap?: string;
|
|
65
|
+
}
|
|
28
66
|
|
|
29
67
|
/**
|
|
30
|
-
*
|
|
68
|
+
* createDjangoSitemap — factory for Next.js `app/sitemap.ts`.
|
|
69
|
+
*
|
|
70
|
+
* Backed by `django_cfg.modules.django_sitemap`: the backend exposes a
|
|
71
|
+
* paginated JSON feed, this factory turns each chunk into a Next.js
|
|
72
|
+
* sitemap file via `generateSitemaps()`.
|
|
31
73
|
*
|
|
32
74
|
* Usage:
|
|
33
|
-
* ```tsx
|
|
34
|
-
* // app/sitemap.ts
|
|
35
|
-
* import { createSitemapHandler } from '@djangocfg/nextjs/sitemap';
|
|
36
75
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* // apps/<app>/app/sitemap.ts
|
|
78
|
+
* import { createDjangoSitemap } from '@djangocfg/nextjs/sitemap';
|
|
79
|
+
*
|
|
80
|
+
* const { generateSitemaps, sitemap } = createDjangoSitemap({
|
|
81
|
+
* host: process.env.NEXT_PUBLIC_SITE_URL!,
|
|
82
|
+
* apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
83
|
+
* staticRoutes: [
|
|
84
|
+
* { path: '/', changeFrequency: 'daily', priority: 1.0 },
|
|
85
|
+
* { path: '/catalog', changeFrequency: 'daily', priority: 0.9 },
|
|
42
86
|
* ],
|
|
43
|
-
* dynamicPages: async () => {
|
|
44
|
-
* const posts = await fetchPosts();
|
|
45
|
-
* return posts.map(post => ({
|
|
46
|
-
* loc: `/posts/${post.slug}`,
|
|
47
|
-
* lastmod: post.updatedAt,
|
|
48
|
-
* changefreq: 'weekly',
|
|
49
|
-
* priority: 0.7,
|
|
50
|
-
* }));
|
|
51
|
-
* },
|
|
52
|
-
* // i18n support with hreflang
|
|
53
|
-
* i18n: {
|
|
54
|
-
* locales: ['en', 'ru', 'ko'],
|
|
55
|
-
* defaultLocale: 'en',
|
|
56
|
-
* },
|
|
57
87
|
* });
|
|
88
|
+
*
|
|
89
|
+
* export { generateSitemaps, sitemap as default };
|
|
90
|
+
* export const revalidate = 3600;
|
|
58
91
|
* ```
|
|
92
|
+
*
|
|
93
|
+
* Without `apiUrl`, the sitemap emits only the static routes — handy for
|
|
94
|
+
* marketing sites that don't have a backend-driven URL space.
|
|
59
95
|
*/
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
interface SitemapApi {
|
|
98
|
+
generateSitemaps: () => Promise<Array<{
|
|
99
|
+
id: string;
|
|
100
|
+
}>>;
|
|
101
|
+
sitemap: (props: {
|
|
102
|
+
id: Promise<string>;
|
|
103
|
+
}) => Promise<MetadataRoute.Sitemap>;
|
|
104
|
+
}
|
|
105
|
+
declare function createDjangoSitemap(opts: CreateDjangoSitemapOptions): SitemapApi;
|
|
62
106
|
|
|
63
107
|
/**
|
|
64
|
-
*
|
|
108
|
+
* createRobots — factory for Next.js `app/robots.ts`.
|
|
109
|
+
*
|
|
110
|
+
* Emits a single `User-Agent: *` block plus a Sitemap pointer. Disallow
|
|
111
|
+
* defaults cover the common cases (auth, account, API surface) — override
|
|
112
|
+
* per app if needed.
|
|
65
113
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
114
|
+
* Usage:
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* // apps/<app>/app/robots.ts
|
|
118
|
+
* import { createRobots } from '@djangocfg/nextjs/sitemap';
|
|
119
|
+
*
|
|
120
|
+
* export default createRobots({
|
|
121
|
+
* host: process.env.NEXT_PUBLIC_SITE_URL!,
|
|
122
|
+
* disallow: ['/account/', '/auth', '/api/', '/apix/'],
|
|
123
|
+
* });
|
|
124
|
+
* ```
|
|
68
125
|
*/
|
|
69
126
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
i18n?: SitemapI18nOptions;
|
|
73
|
-
siteUrl: string;
|
|
74
|
-
}
|
|
127
|
+
declare function createRobots(opts: CreateRobotsOptions): () => MetadataRoute.Robots;
|
|
128
|
+
|
|
75
129
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
130
|
+
* Sitemap chunk id encoding.
|
|
131
|
+
*
|
|
132
|
+
* Each Next.js sitemap file is addressed by an opaque string id. We
|
|
133
|
+
* round-trip a `(source, cursor)` pair through that id so the default
|
|
134
|
+
* sitemap handler knows which Django chunk to fetch.
|
|
135
|
+
*
|
|
136
|
+
* Separator `--` is unambiguous: backend cursors use URL-safe base64 (`-_`).
|
|
78
137
|
*/
|
|
79
|
-
declare function
|
|
138
|
+
declare function encodeChunkId(source: string, cursor: string | null): string;
|
|
139
|
+
declare function decodeChunkId(id: string): {
|
|
140
|
+
source: string;
|
|
141
|
+
cursor: string | null;
|
|
142
|
+
};
|
|
143
|
+
|
|
80
144
|
/**
|
|
81
|
-
*
|
|
145
|
+
* Sitemap fetch helpers — never throw.
|
|
146
|
+
*
|
|
147
|
+
* Failures are converted to empty payloads so a transient Django outage
|
|
148
|
+
* doesn't break `next build`. The next ISR cycle (`revalidate`) recovers
|
|
149
|
+
* automatically.
|
|
82
150
|
*/
|
|
83
|
-
declare function normalizeUrl(url: string, siteUrl: string): string;
|
|
84
151
|
|
|
85
|
-
|
|
152
|
+
declare function fetchSitemapIndex(apiUrl: string, revalidate: number): Promise<SitemapIndex>;
|
|
153
|
+
declare function fetchSitemapFeed(apiUrl: string, source: string, cursor: string | null, revalidate: number): Promise<SitemapFeedPage>;
|
|
154
|
+
|
|
155
|
+
export { type CreateDjangoSitemapOptions, type CreateRobotsOptions, type SitemapChunkInfo, type SitemapEntry, type SitemapFeedPage, type SitemapIndex, type SitemapSourceInfo, type StaticRoute, createDjangoSitemap, createRobots, decodeChunkId, encodeChunkId, fetchSitemapFeed, fetchSitemapIndex };
|
package/dist/sitemap/index.mjs
CHANGED
|
@@ -1,152 +1,125 @@
|
|
|
1
|
-
// src/sitemap/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<loc>${escapeXml(loc)}</loc>${hreflangLinks}
|
|
24
|
-
${lastmod ? `<lastmod>${formatDate(lastmod)}</lastmod>` : ""}
|
|
25
|
-
${changefreq ? `<changefreq>${changefreq}</changefreq>` : ""}
|
|
26
|
-
${priority !== void 0 ? `<priority>${priority.toFixed(1)}</priority>` : ""}
|
|
27
|
-
</url>`;
|
|
28
|
-
}).join("\n")}
|
|
29
|
-
</urlset>`;
|
|
30
|
-
}
|
|
31
|
-
function generateHreflangLinks(loc, i18n, siteUrl) {
|
|
32
|
-
const { locales, defaultLocale } = i18n;
|
|
33
|
-
const baseSiteUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
|
|
34
|
-
let path = loc.replace(baseSiteUrl, "");
|
|
35
|
-
for (const locale of locales) {
|
|
36
|
-
const localePrefix = `/${locale}`;
|
|
37
|
-
if (path === localePrefix || path.startsWith(`${localePrefix}/`)) {
|
|
38
|
-
path = path.slice(localePrefix.length) || "/";
|
|
39
|
-
break;
|
|
1
|
+
// src/sitemap/fetch.ts
|
|
2
|
+
var EMPTY_INDEX = {
|
|
3
|
+
sources: [],
|
|
4
|
+
generated_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
5
|
+
ttl_seconds: 60
|
|
6
|
+
};
|
|
7
|
+
var emptyFeed = (source, cursor) => ({
|
|
8
|
+
source,
|
|
9
|
+
chunk_id: `${source}-empty`,
|
|
10
|
+
count: 0,
|
|
11
|
+
has_more: false,
|
|
12
|
+
next_cursor: cursor,
|
|
13
|
+
entries: []
|
|
14
|
+
});
|
|
15
|
+
async function fetchSitemapIndex(apiUrl, revalidate) {
|
|
16
|
+
try {
|
|
17
|
+
const r = await fetch(`${apiUrl}/cfg/sitemap/index/`, {
|
|
18
|
+
next: { revalidate }
|
|
19
|
+
});
|
|
20
|
+
if (!r.ok) {
|
|
21
|
+
console.warn(`[sitemap] index fetch returned ${r.status}; falling back to empty`);
|
|
22
|
+
return EMPTY_INDEX;
|
|
40
23
|
}
|
|
24
|
+
return await r.json();
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn("[sitemap] index fetch failed:", err);
|
|
27
|
+
return EMPTY_INDEX;
|
|
41
28
|
}
|
|
42
|
-
const links = [];
|
|
43
|
-
for (const locale of locales) {
|
|
44
|
-
const localePath = locale === defaultLocale ? path : path === "/" ? `/${locale}` : `/${locale}${path}`;
|
|
45
|
-
const fullUrl = `${baseSiteUrl}${localePath}`;
|
|
46
|
-
links.push(
|
|
47
|
-
` <xhtml:link rel="alternate" hreflang="${locale}" href="${escapeXml(fullUrl)}"/>`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
const defaultUrl = `${baseSiteUrl}${path}`;
|
|
51
|
-
links.push(
|
|
52
|
-
` <xhtml:link rel="alternate" hreflang="x-default" href="${escapeXml(defaultUrl)}"/>`
|
|
53
|
-
);
|
|
54
|
-
return "\n" + links.join("\n");
|
|
55
29
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
30
|
+
async function fetchSitemapFeed(apiUrl, source, cursor, revalidate) {
|
|
31
|
+
const params = new URLSearchParams({ source });
|
|
32
|
+
if (cursor) params.set("cursor", cursor);
|
|
33
|
+
try {
|
|
34
|
+
const r = await fetch(`${apiUrl}/cfg/sitemap/feed/?${params}`, {
|
|
35
|
+
next: { revalidate }
|
|
36
|
+
});
|
|
37
|
+
if (!r.ok) {
|
|
38
|
+
console.warn(`[sitemap] feed ${source} returned ${r.status}; falling back to empty`);
|
|
39
|
+
return emptyFeed(source, cursor);
|
|
40
|
+
}
|
|
41
|
+
return await r.json();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.warn(`[sitemap] feed ${source} fetch failed:`, err);
|
|
44
|
+
return emptyFeed(source, cursor);
|
|
59
45
|
}
|
|
60
|
-
return date.toISOString().split("T")[0];
|
|
61
46
|
}
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
|
|
48
|
+
// src/sitemap/ids.ts
|
|
49
|
+
function encodeChunkId(source, cursor) {
|
|
50
|
+
return `${source}--${cursor ?? ""}`;
|
|
64
51
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
return `${baseUrl}${path}`;
|
|
52
|
+
function decodeChunkId(id) {
|
|
53
|
+
const sep = id.indexOf("--");
|
|
54
|
+
if (sep === -1) return { source: id, cursor: null };
|
|
55
|
+
const source = id.slice(0, sep);
|
|
56
|
+
const cursorRaw = id.slice(sep + 2);
|
|
57
|
+
return { source, cursor: cursorRaw === "" ? null : cursorRaw };
|
|
72
58
|
}
|
|
73
59
|
|
|
74
|
-
// src/sitemap/
|
|
75
|
-
|
|
60
|
+
// src/sitemap/sitemap.ts
|
|
61
|
+
var STATIC_ID = "static";
|
|
62
|
+
var DEFAULT_INDEX_REVALIDATE = 600;
|
|
63
|
+
var DEFAULT_FEED_REVALIDATE = 3600;
|
|
64
|
+
function createDjangoSitemap(opts) {
|
|
76
65
|
const {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
} =
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
66
|
+
host,
|
|
67
|
+
apiUrl,
|
|
68
|
+
staticRoutes = [],
|
|
69
|
+
indexRevalidate = DEFAULT_INDEX_REVALIDATE,
|
|
70
|
+
feedRevalidate = DEFAULT_FEED_REVALIDATE
|
|
71
|
+
} = opts;
|
|
72
|
+
return {
|
|
73
|
+
async generateSitemaps() {
|
|
74
|
+
const ids = [{ id: STATIC_ID }];
|
|
75
|
+
if (!apiUrl) return ids;
|
|
76
|
+
const index = await fetchSitemapIndex(apiUrl, indexRevalidate);
|
|
77
|
+
for (const s of index.sources) {
|
|
78
|
+
for (const c of s.chunks) {
|
|
79
|
+
ids.push({ id: encodeChunkId(s.name, c.cursor_to) });
|
|
80
|
+
}
|
|
91
81
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
return ids;
|
|
83
|
+
},
|
|
84
|
+
async sitemap({ id: idPromise }) {
|
|
85
|
+
const id = await idPromise;
|
|
86
|
+
if (id === STATIC_ID) {
|
|
87
|
+
return renderStatic(host, staticRoutes);
|
|
88
|
+
}
|
|
89
|
+
if (!apiUrl) return [];
|
|
90
|
+
const { source, cursor } = decodeChunkId(id);
|
|
91
|
+
const feed = await fetchSitemapFeed(apiUrl, source, cursor, feedRevalidate);
|
|
92
|
+
return feed.entries.map((e) => ({
|
|
93
|
+
url: `${host}${e.loc}`,
|
|
94
|
+
lastModified: e.lastmod ? new Date(e.lastmod) : void 0
|
|
100
95
|
}));
|
|
101
96
|
}
|
|
102
|
-
const sitemap = generateSitemapXml({
|
|
103
|
-
urls: expandedUrls,
|
|
104
|
-
i18n,
|
|
105
|
-
siteUrl
|
|
106
|
-
});
|
|
107
|
-
return new NextResponse(sitemap, {
|
|
108
|
-
status: 200,
|
|
109
|
-
headers: {
|
|
110
|
-
"Content-Type": "application/xml",
|
|
111
|
-
"Cache-Control": cacheControl
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
97
|
};
|
|
115
98
|
}
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
loc: normalizeUrl(path, siteUrl)
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
for (const locale of locales) {
|
|
137
|
-
const localePath = locale === defaultLocale ? path : path === "/" ? `/${locale}` : `/${locale}${path}`;
|
|
138
|
-
expandedUrls.push({
|
|
139
|
-
...url,
|
|
140
|
-
loc: `${baseSiteUrl}${localePath}`
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return expandedUrls;
|
|
99
|
+
function renderStatic(host, routes) {
|
|
100
|
+
return routes.map((r) => ({
|
|
101
|
+
url: `${host}${r.path}`,
|
|
102
|
+
changeFrequency: r.changeFrequency,
|
|
103
|
+
priority: r.priority
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/sitemap/robots.ts
|
|
108
|
+
var DEFAULT_DISALLOW = ["/account/", "/auth", "/api/"];
|
|
109
|
+
function createRobots(opts) {
|
|
110
|
+
const { host, disallow = DEFAULT_DISALLOW, sitemap } = opts;
|
|
111
|
+
return () => ({
|
|
112
|
+
rules: [{ userAgent: "*", allow: "/", disallow }],
|
|
113
|
+
sitemap: sitemap ?? `${host}/sitemap.xml`,
|
|
114
|
+
host
|
|
115
|
+
});
|
|
146
116
|
}
|
|
147
117
|
export {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
118
|
+
createDjangoSitemap,
|
|
119
|
+
createRobots,
|
|
120
|
+
decodeChunkId,
|
|
121
|
+
encodeChunkId,
|
|
122
|
+
fetchSitemapFeed,
|
|
123
|
+
fetchSitemapIndex
|
|
151
124
|
};
|
|
152
125
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sitemap/route.ts","../../src/sitemap/generator.ts"],"sourcesContent":["/**\n * Sitemap Route Handler for Next.js App Router\n *\n * Usage:\n * ```tsx\n * // app/sitemap.ts\n * import { createSitemapHandler } from '@djangocfg/nextjs/sitemap';\n *\n * export default createSitemapHandler({\n * siteUrl: 'https://example.com',\n * staticPages: [\n * { loc: '/', changefreq: 'daily', priority: 1.0 },\n * { loc: '/about', changefreq: 'monthly', priority: 0.8 },\n * ],\n * dynamicPages: async () => {\n * const posts = await fetchPosts();\n * return posts.map(post => ({\n * loc: `/posts/${post.slug}`,\n * lastmod: post.updatedAt,\n * changefreq: 'weekly',\n * priority: 0.7,\n * }));\n * },\n * // i18n support with hreflang\n * i18n: {\n * locales: ['en', 'ru', 'ko'],\n * defaultLocale: 'en',\n * },\n * });\n * ```\n */\n\nimport { NextResponse } from 'next/server';\n\nimport { generateSitemapXml, normalizeUrl } from './generator';\n\nimport type { SitemapGeneratorOptions } from './types';\nimport type { SitemapUrl } from '../types';\n\nexport function createSitemapHandler(options: SitemapGeneratorOptions) {\n const {\n siteUrl,\n staticPages = [],\n dynamicPages = [],\n cacheControl = 'public, s-maxage=86400, stale-while-revalidate',\n i18n,\n } = options;\n\n return async function GET() {\n const urls: SitemapUrl[] = [...staticPages];\n\n // Add dynamic pages\n if (dynamicPages) {\n if (typeof dynamicPages === 'function') {\n const dynamicUrls = await dynamicPages();\n urls.push(...dynamicUrls);\n } else {\n urls.push(...dynamicPages);\n }\n }\n\n // Expand URLs for each locale if i18n is enabled\n let expandedUrls: SitemapUrl[];\n if (i18n) {\n expandedUrls = expandUrlsForLocales(urls, i18n.locales, siteUrl, i18n.defaultLocale);\n } else {\n expandedUrls = urls.map((url) => ({\n ...url,\n loc: normalizeUrl(url.loc, siteUrl),\n }));\n }\n\n // Generate XML with i18n support\n const sitemap = generateSitemapXml({\n urls: expandedUrls,\n i18n,\n siteUrl,\n });\n\n // Return response\n return new NextResponse(sitemap, {\n status: 200,\n headers: {\n 'Content-Type': 'application/xml',\n 'Cache-Control': cacheControl,\n },\n });\n };\n}\n\n/**\n * Expand URLs to include all locale variants\n * Input: [{ loc: '/page' }] with locales ['en', 'ru']\n * Output: [{ loc: 'https://example.com/en/page' }, { loc: 'https://example.com/ru/page' }]\n */\nfunction expandUrlsForLocales(\n urls: SitemapUrl[],\n locales: string[],\n siteUrl: string,\n defaultLocale?: string\n): SitemapUrl[] {\n const baseSiteUrl = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;\n const expandedUrls: SitemapUrl[] = [];\n\n for (const url of urls) {\n // Normalize the path (remove leading slash if present for consistency)\n let path = url.loc;\n if (path.startsWith(baseSiteUrl)) {\n path = path.replace(baseSiteUrl, '');\n }\n if (!path.startsWith('/')) {\n path = '/' + path;\n }\n\n // Check if the path already has a locale prefix\n const hasLocalePrefix = locales.some(\n (locale) => path === `/${locale}` || path.startsWith(`/${locale}/`)\n );\n\n if (hasLocalePrefix) {\n // URL already has locale prefix, just normalize it\n expandedUrls.push({\n ...url,\n loc: normalizeUrl(path, siteUrl),\n });\n } else {\n // Create a URL for each locale\n // Default locale gets no prefix (localePrefix: 'as-needed')\n for (const locale of locales) {\n const localePath =\n locale === defaultLocale\n ? path\n : path === '/'\n ? `/${locale}`\n : `/${locale}${path}`;\n expandedUrls.push({\n ...url,\n loc: `${baseSiteUrl}${localePath}`,\n });\n }\n }\n }\n\n return expandedUrls;\n}\n\n","/**\n * Sitemap Generator\n *\n * Generates XML sitemap from configuration\n * Supports i18n with hreflang tags for multilingual sites\n */\n\nimport type { SitemapUrl } from '../types';\nimport type { SitemapI18nOptions } from './types';\n\nexport interface GenerateSitemapXmlOptions {\n urls: SitemapUrl[];\n i18n?: SitemapI18nOptions;\n siteUrl: string;\n}\n\n/**\n * Generate XML sitemap string from URLs\n * Supports i18n with hreflang alternate links\n */\nexport function generateSitemapXml(\n urlsOrOptions: SitemapUrl[] | GenerateSitemapXmlOptions\n): string {\n // Support both old signature (just urls) and new signature (options object)\n const isOptionsObject = !Array.isArray(urlsOrOptions);\n const urls = isOptionsObject ? urlsOrOptions.urls : urlsOrOptions;\n const i18n = isOptionsObject ? urlsOrOptions.i18n : undefined;\n const siteUrl = isOptionsObject ? urlsOrOptions.siteUrl : '';\n\n // Add xhtml namespace if i18n is enabled\n const namespaces = i18n\n ? `xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\"`\n : `xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9\n http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\"`;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset ${namespaces}>\n${urls\n .map(({ loc, lastmod, changefreq, priority }) => {\n const hreflangLinks = i18n\n ? generateHreflangLinks(loc, i18n, siteUrl)\n : '';\n\n return ` <url>\n <loc>${escapeXml(loc)}</loc>${hreflangLinks}\n ${lastmod ? `<lastmod>${formatDate(lastmod)}</lastmod>` : ''}\n ${changefreq ? `<changefreq>${changefreq}</changefreq>` : ''}\n ${priority !== undefined ? `<priority>${priority.toFixed(1)}</priority>` : ''}\n </url>`;\n })\n .join('\\n')}\n</urlset>`;\n}\n\n/**\n * Generate hreflang links for a URL\n */\nfunction generateHreflangLinks(\n loc: string,\n i18n: SitemapI18nOptions,\n siteUrl: string\n): string {\n const { locales, defaultLocale } = i18n;\n\n // Extract the path without locale prefix from the URL\n // e.g., https://example.com/en/page -> /page\n const baseSiteUrl = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;\n let path = loc.replace(baseSiteUrl, '');\n\n // Remove locale prefix if present\n for (const locale of locales) {\n const localePrefix = `/${locale}`;\n if (path === localePrefix || path.startsWith(`${localePrefix}/`)) {\n path = path.slice(localePrefix.length) || '/';\n break;\n }\n }\n\n const links: string[] = [];\n\n // Add hreflang for each locale\n // Default locale gets no prefix (localePrefix: 'as-needed')\n for (const locale of locales) {\n const localePath =\n locale === defaultLocale\n ? path\n : path === '/'\n ? `/${locale}`\n : `/${locale}${path}`;\n const fullUrl = `${baseSiteUrl}${localePath}`;\n links.push(\n ` <xhtml:link rel=\"alternate\" hreflang=\"${locale}\" href=\"${escapeXml(fullUrl)}\"/>`\n );\n }\n\n // Add x-default pointing to default locale (no prefix)\n const defaultUrl = `${baseSiteUrl}${path}`;\n links.push(\n ` <xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"${escapeXml(defaultUrl)}\"/>`\n );\n\n return '\\n' + links.join('\\n');\n}\n\n/**\n * Format date for sitemap (ISO 8601)\n */\nfunction formatDate(date: string | Date): string {\n if (typeof date === 'string') {\n return date;\n }\n return date.toISOString().split('T')[0];\n}\n\n/**\n * Escape XML special characters\n */\nfunction escapeXml(unsafe: string): string {\n return unsafe\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Normalize URL (ensure absolute)\n */\nexport function normalizeUrl(url: string, siteUrl: string): string {\n if (url.startsWith('http://') || url.startsWith('https://')) {\n return url;\n }\n const baseUrl = siteUrl.endsWith('/') ? siteUrl.slice(0, -1) : siteUrl;\n const path = url.startsWith('/') ? url : `/${url}`;\n return `${baseUrl}${path}`;\n}\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/sitemap/fetch.ts","../../src/sitemap/ids.ts","../../src/sitemap/sitemap.ts","../../src/sitemap/robots.ts"],"sourcesContent":["/**\n * Sitemap fetch helpers — never throw.\n *\n * Failures are converted to empty payloads so a transient Django outage\n * doesn't break `next build`. The next ISR cycle (`revalidate`) recovers\n * automatically.\n */\n\nimport type { SitemapFeedPage, SitemapIndex } from './types';\n\nconst EMPTY_INDEX: SitemapIndex = {\n sources: [],\n generated_at: new Date(0).toISOString(),\n ttl_seconds: 60,\n};\n\nconst emptyFeed = (source: string, cursor: string | null): SitemapFeedPage => ({\n source,\n chunk_id: `${source}-empty`,\n count: 0,\n has_more: false,\n next_cursor: cursor,\n entries: [],\n});\n\nexport async function fetchSitemapIndex(\n apiUrl: string,\n revalidate: number,\n): Promise<SitemapIndex> {\n try {\n const r = await fetch(`${apiUrl}/cfg/sitemap/index/`, {\n next: { revalidate },\n });\n if (!r.ok) {\n console.warn(`[sitemap] index fetch returned ${r.status}; falling back to empty`);\n return EMPTY_INDEX;\n }\n return (await r.json()) as SitemapIndex;\n } catch (err) {\n console.warn('[sitemap] index fetch failed:', err);\n return EMPTY_INDEX;\n }\n}\n\nexport async function fetchSitemapFeed(\n apiUrl: string,\n source: string,\n cursor: string | null,\n revalidate: number,\n): Promise<SitemapFeedPage> {\n const params = new URLSearchParams({ source });\n if (cursor) params.set('cursor', cursor);\n try {\n const r = await fetch(`${apiUrl}/cfg/sitemap/feed/?${params}`, {\n next: { revalidate },\n });\n if (!r.ok) {\n console.warn(`[sitemap] feed ${source} returned ${r.status}; falling back to empty`);\n return emptyFeed(source, cursor);\n }\n return (await r.json()) as SitemapFeedPage;\n } catch (err) {\n console.warn(`[sitemap] feed ${source} fetch failed:`, err);\n return emptyFeed(source, cursor);\n }\n}\n","/**\n * Sitemap chunk id encoding.\n *\n * Each Next.js sitemap file is addressed by an opaque string id. We\n * round-trip a `(source, cursor)` pair through that id so the default\n * sitemap handler knows which Django chunk to fetch.\n *\n * Separator `--` is unambiguous: backend cursors use URL-safe base64 (`-_`).\n */\n\nexport function encodeChunkId(source: string, cursor: string | null): string {\n return `${source}--${cursor ?? ''}`;\n}\n\nexport function decodeChunkId(id: string): { source: string; cursor: string | null } {\n const sep = id.indexOf('--');\n if (sep === -1) return { source: id, cursor: null };\n const source = id.slice(0, sep);\n const cursorRaw = id.slice(sep + 2);\n return { source, cursor: cursorRaw === '' ? null : cursorRaw };\n}\n","/**\n * createDjangoSitemap — factory for Next.js `app/sitemap.ts`.\n *\n * Backed by `django_cfg.modules.django_sitemap`: the backend exposes a\n * paginated JSON feed, this factory turns each chunk into a Next.js\n * sitemap file via `generateSitemaps()`.\n *\n * Usage:\n *\n * ```ts\n * // apps/<app>/app/sitemap.ts\n * import { createDjangoSitemap } from '@djangocfg/nextjs/sitemap';\n *\n * const { generateSitemaps, sitemap } = createDjangoSitemap({\n * host: process.env.NEXT_PUBLIC_SITE_URL!,\n * apiUrl: process.env.NEXT_PUBLIC_API_URL!,\n * staticRoutes: [\n * { path: '/', changeFrequency: 'daily', priority: 1.0 },\n * { path: '/catalog', changeFrequency: 'daily', priority: 0.9 },\n * ],\n * });\n *\n * export { generateSitemaps, sitemap as default };\n * export const revalidate = 3600;\n * ```\n *\n * Without `apiUrl`, the sitemap emits only the static routes — handy for\n * marketing sites that don't have a backend-driven URL space.\n */\n\nimport type { MetadataRoute } from 'next';\n\nimport { fetchSitemapFeed, fetchSitemapIndex } from './fetch';\nimport { decodeChunkId, encodeChunkId } from './ids';\n\nimport type { CreateDjangoSitemapOptions, StaticRoute } from './types';\n\nconst STATIC_ID = 'static';\nconst DEFAULT_INDEX_REVALIDATE = 600;\nconst DEFAULT_FEED_REVALIDATE = 3600;\n\ninterface SitemapApi {\n generateSitemaps: () => Promise<Array<{ id: string }>>;\n sitemap: (props: { id: Promise<string> }) => Promise<MetadataRoute.Sitemap>;\n}\n\nexport function createDjangoSitemap(opts: CreateDjangoSitemapOptions): SitemapApi {\n const {\n host,\n apiUrl,\n staticRoutes = [],\n indexRevalidate = DEFAULT_INDEX_REVALIDATE,\n feedRevalidate = DEFAULT_FEED_REVALIDATE,\n } = opts;\n\n return {\n async generateSitemaps() {\n const ids: Array<{ id: string }> = [{ id: STATIC_ID }];\n if (!apiUrl) return ids;\n const index = await fetchSitemapIndex(apiUrl, indexRevalidate);\n for (const s of index.sources) {\n for (const c of s.chunks) {\n ids.push({ id: encodeChunkId(s.name, c.cursor_to) });\n }\n }\n return ids;\n },\n\n async sitemap({ id: idPromise }) {\n const id = await idPromise;\n\n if (id === STATIC_ID) {\n return renderStatic(host, staticRoutes);\n }\n\n if (!apiUrl) return [];\n\n const { source, cursor } = decodeChunkId(id);\n const feed = await fetchSitemapFeed(apiUrl, source, cursor, feedRevalidate);\n return feed.entries.map((e) => ({\n url: `${host}${e.loc}`,\n lastModified: e.lastmod ? new Date(e.lastmod) : undefined,\n }));\n },\n };\n}\n\nfunction renderStatic(host: string, routes: StaticRoute[]): MetadataRoute.Sitemap {\n return routes.map((r) => ({\n url: `${host}${r.path}`,\n changeFrequency: r.changeFrequency,\n priority: r.priority,\n }));\n}\n","/**\n * createRobots — factory for Next.js `app/robots.ts`.\n *\n * Emits a single `User-Agent: *` block plus a Sitemap pointer. Disallow\n * defaults cover the common cases (auth, account, API surface) — override\n * per app if needed.\n *\n * Usage:\n *\n * ```ts\n * // apps/<app>/app/robots.ts\n * import { createRobots } from '@djangocfg/nextjs/sitemap';\n *\n * export default createRobots({\n * host: process.env.NEXT_PUBLIC_SITE_URL!,\n * disallow: ['/account/', '/auth', '/api/', '/apix/'],\n * });\n * ```\n */\n\nimport type { MetadataRoute } from 'next';\n\nimport type { CreateRobotsOptions } from './types';\n\nconst DEFAULT_DISALLOW = ['/account/', '/auth', '/api/'];\n\nexport function createRobots(opts: CreateRobotsOptions): () => MetadataRoute.Robots {\n const { host, disallow = DEFAULT_DISALLOW, sitemap } = opts;\n return () => ({\n rules: [{ userAgent: '*', allow: '/', disallow }],\n sitemap: sitemap ?? `${host}/sitemap.xml`,\n host,\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/nextjs",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.415",
|
|
4
4
|
"description": "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -143,9 +143,9 @@
|
|
|
143
143
|
"ai-docs": "tsx src/ai/cli.ts"
|
|
144
144
|
},
|
|
145
145
|
"peerDependencies": {
|
|
146
|
-
"@djangocfg/i18n": "^2.1.
|
|
147
|
-
"@djangocfg/monitor": "^2.1.
|
|
148
|
-
"@djangocfg/ui-core": "^2.1.
|
|
146
|
+
"@djangocfg/i18n": "^2.1.415",
|
|
147
|
+
"@djangocfg/monitor": "^2.1.415",
|
|
148
|
+
"@djangocfg/ui-core": "^2.1.415",
|
|
149
149
|
"next": "^16.2.2"
|
|
150
150
|
},
|
|
151
151
|
"peerDependenciesMeta": {
|
|
@@ -167,11 +167,11 @@
|
|
|
167
167
|
"serwist": "^9.2.3"
|
|
168
168
|
},
|
|
169
169
|
"devDependencies": {
|
|
170
|
-
"@djangocfg/i18n": "^2.1.
|
|
171
|
-
"@djangocfg/monitor": "^2.1.
|
|
172
|
-
"@djangocfg/ui-core": "^2.1.
|
|
173
|
-
"@djangocfg/layouts": "^2.1.
|
|
174
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
170
|
+
"@djangocfg/i18n": "^2.1.415",
|
|
171
|
+
"@djangocfg/monitor": "^2.1.415",
|
|
172
|
+
"@djangocfg/ui-core": "^2.1.415",
|
|
173
|
+
"@djangocfg/layouts": "^2.1.415",
|
|
174
|
+
"@djangocfg/typescript-config": "^2.1.415",
|
|
175
175
|
"@types/node": "^25.2.3",
|
|
176
176
|
"@types/react": "^19.2.15",
|
|
177
177
|
"@types/react-dom": "^19.2.3",
|