next-advanced-sitemap 1.0.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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/index.cjs +162 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fomadev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# next-advanced-sitemap
|
|
2
|
+
|
|
3
|
+
A robust and type-safe sitemap generator for Next.js (App Router). This library extends standard sitemap capabilities by providing native support for Google-specific metadata including Images, Videos, News, and Internationalization (Hreflang).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
While Next.js provides a built-in `MetadataRoute.Sitemap` utility, it currently lacks support for advanced SEO attributes required by high-performance web applications. `next-advanced-sitemap` bridges this gap, allowing developers to programmatically generate complex XML sitemaps that comply with Google's extended schemas.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Google Images Support**: Index visual assets such as dashboard charts, infographics, and banners.
|
|
12
|
+
- **Google Video Support**: Improve search visibility for video content with thumbnail and description metadata.
|
|
13
|
+
- **Google News Support**: Comply with Google News requirements including publication names and dates.
|
|
14
|
+
- **Internationalization**: Seamless integration of `xhtml:link` tags for Hreflang and multi-regional SEO.
|
|
15
|
+
- **Developer Experience**: Fully typed with TypeScript, zero external dependencies, and optimized for Next.js Route Handlers.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install next-advanced-sitemap
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
To implement an advanced sitemap in the Next.js App Router, create a Route Handler at `app/sitemap.xml/route.ts`.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { getServerSitemapResponse } from 'next-advanced-sitemap';
|
|
29
|
+
|
|
30
|
+
export async function GET() {
|
|
31
|
+
const entries = [
|
|
32
|
+
{
|
|
33
|
+
url: 'https://fomadev.com',
|
|
34
|
+
lastmod: new Date(),
|
|
35
|
+
changefreq: 'daily',
|
|
36
|
+
priority: 1.0,
|
|
37
|
+
alternates: [
|
|
38
|
+
{ hreflang: 'fr', href: 'https://fomadev.com/fr' },
|
|
39
|
+
{ hreflang: 'en', href: 'https://fomadev.com/en' }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
url: 'https://fomadev.com/dashboard',
|
|
44
|
+
images: [
|
|
45
|
+
{
|
|
46
|
+
loc: 'https://fomadev.com/charts/analytics.png',
|
|
47
|
+
title: 'Growth Analytics Chart',
|
|
48
|
+
caption: 'Visual representation of monthly user growth.'
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
url: 'https://fomadev.com/video-tutorial',
|
|
54
|
+
videos: [
|
|
55
|
+
{
|
|
56
|
+
thumbnail_loc: 'https://fomadev.com/thumbs/tutorial.jpg',
|
|
57
|
+
title: 'Next.js Advanced SEO Tutorial',
|
|
58
|
+
description: 'Learn how to implement advanced sitemaps in Next.js.',
|
|
59
|
+
publication_date: new Date('2026-04-22')
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
return getServerSitemapResponse(entries);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## API Reference
|
|
70
|
+
|
|
71
|
+
### getServerSitemapResponse(entries: SitemapEntry[])
|
|
72
|
+
|
|
73
|
+
Generates a standard Next.js `Response` object with the correct `application/xml` content-type and optimized cache headers.
|
|
74
|
+
|
|
75
|
+
### SitemapEntry Object
|
|
76
|
+
|
|
77
|
+
<table>
|
|
78
|
+
<thead>
|
|
79
|
+
<tr>
|
|
80
|
+
<th>Property</th>
|
|
81
|
+
<th>Type</th>
|
|
82
|
+
<th>Description</th>
|
|
83
|
+
</tr>
|
|
84
|
+
</thead>
|
|
85
|
+
<tbody>
|
|
86
|
+
<tr>
|
|
87
|
+
<td><code>url</code></td>
|
|
88
|
+
<td>string</td>
|
|
89
|
+
<td>The absolute URL of the page.</td>
|
|
90
|
+
</tr>
|
|
91
|
+
<tr>
|
|
92
|
+
<td><code>lastmod</code></td>
|
|
93
|
+
<td>Date | string</td>
|
|
94
|
+
<td>(Optional) Last modification date in ISO format.</td>
|
|
95
|
+
</tr>
|
|
96
|
+
<tr>
|
|
97
|
+
<td><code>changefreq</code></td>
|
|
98
|
+
<td>string</td>
|
|
99
|
+
<td>(Optional) Search engine hint for crawl frequency.</td>
|
|
100
|
+
</tr>
|
|
101
|
+
<tr>
|
|
102
|
+
<td><code>priority</code></td>
|
|
103
|
+
<td>number</td>
|
|
104
|
+
<td>(Optional) Priority of the URL (0.0 to 1.0).</td>
|
|
105
|
+
</tr>
|
|
106
|
+
<tr>
|
|
107
|
+
<td><code>images</code></td>
|
|
108
|
+
<td>SitemapImage[]</td>
|
|
109
|
+
<td>(Optional) Array of image metadata for Google Images.</td>
|
|
110
|
+
</tr>
|
|
111
|
+
<tr>
|
|
112
|
+
<td><code>videos</code></td>
|
|
113
|
+
<td>SitemapVideo[]</td>
|
|
114
|
+
<td>(Optional) Array of video metadata for Google Videos.</td>
|
|
115
|
+
</tr>
|
|
116
|
+
<tr>
|
|
117
|
+
<td><code>news</code></td>
|
|
118
|
+
<td>SitemapNews</td>
|
|
119
|
+
<td>(Optional) Metadata for Google News indexing.</td>
|
|
120
|
+
</tr>
|
|
121
|
+
<tr>
|
|
122
|
+
<td><code>alternates</code></td>
|
|
123
|
+
<td>SitemapAlternate[]</td>
|
|
124
|
+
<td>(Optional) Language and regional alternate URLs (Hreflang).</td>
|
|
125
|
+
</tr>
|
|
126
|
+
</tbody>
|
|
127
|
+
</table>
|
|
128
|
+
|
|
129
|
+
## Technical Implementation
|
|
130
|
+
|
|
131
|
+
This library uses a stream-aligned string builder approach to ensure minimal memory footprint during XML generation. It automatically handles XML entity escaping for special characters (e.g., `&`, `<`, `>`) to prevent sitemap corruption.
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
Distributed under the MIT License. See <a href="LICENSE">LICENSE</a> for more information.
|
|
136
|
+
|
|
137
|
+
## Author
|
|
138
|
+
|
|
139
|
+
Created by <a href="https://github.com/fomadev">fomadev</a>.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
getServerSitemapResponse: () => getServerSitemapResponse
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/utils/xml-escape.ts
|
|
28
|
+
function escapeXml(unsafe) {
|
|
29
|
+
return unsafe.replace(/[<>&"']/g, (c) => {
|
|
30
|
+
switch (c) {
|
|
31
|
+
case "<":
|
|
32
|
+
return "<";
|
|
33
|
+
case ">":
|
|
34
|
+
return ">";
|
|
35
|
+
case "&":
|
|
36
|
+
return "&";
|
|
37
|
+
case '"':
|
|
38
|
+
return """;
|
|
39
|
+
case "'":
|
|
40
|
+
return "'";
|
|
41
|
+
default:
|
|
42
|
+
return c;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/core/generator.ts
|
|
48
|
+
function generateXml(entries) {
|
|
49
|
+
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
50
|
+
`;
|
|
51
|
+
xml += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
52
|
+
`;
|
|
53
|
+
xml += ` xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
|
54
|
+
`;
|
|
55
|
+
xml += ` xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
|
|
56
|
+
`;
|
|
57
|
+
xml += ` xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
|
|
58
|
+
`;
|
|
59
|
+
xml += ` xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
60
|
+
`;
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
xml += ` <url>
|
|
63
|
+
`;
|
|
64
|
+
xml += ` <loc>${escapeXml(entry.url)}</loc>
|
|
65
|
+
`;
|
|
66
|
+
if (entry.alternates?.length) {
|
|
67
|
+
for (const alt of entry.alternates) {
|
|
68
|
+
xml += ` <xhtml:link rel="alternate" hreflang="${escapeXml(alt.hreflang)}" href="${escapeXml(alt.href)}" />
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (entry.lastmod) {
|
|
73
|
+
const date = entry.lastmod instanceof Date ? entry.lastmod.toISOString() : entry.lastmod;
|
|
74
|
+
xml += ` <lastmod>${date}</lastmod>
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
if (entry.changefreq) {
|
|
78
|
+
xml += ` <changefreq>${entry.changefreq}</changefreq>
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
if (entry.priority !== void 0) {
|
|
82
|
+
xml += ` <priority>${entry.priority.toFixed(1)}</priority>
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
if (entry.images?.length) {
|
|
86
|
+
for (const img of entry.images) {
|
|
87
|
+
xml += ` <image:image>
|
|
88
|
+
`;
|
|
89
|
+
xml += ` <image:loc>${escapeXml(img.loc)}</image:loc>
|
|
90
|
+
`;
|
|
91
|
+
if (img.title) xml += ` <image:title>${escapeXml(img.title)}</image:title>
|
|
92
|
+
`;
|
|
93
|
+
if (img.caption) xml += ` <image:caption>${escapeXml(img.caption)}</image:caption>
|
|
94
|
+
`;
|
|
95
|
+
xml += ` </image:image>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (entry.videos?.length) {
|
|
100
|
+
for (const vid of entry.videos) {
|
|
101
|
+
xml += ` <video:video>
|
|
102
|
+
`;
|
|
103
|
+
xml += ` <video:thumbnail_loc>${escapeXml(vid.thumbnail_loc)}</video:thumbnail_loc>
|
|
104
|
+
`;
|
|
105
|
+
xml += ` <video:title>${escapeXml(vid.title)}</video:title>
|
|
106
|
+
`;
|
|
107
|
+
xml += ` <video:description>${escapeXml(vid.description)}</video:description>
|
|
108
|
+
`;
|
|
109
|
+
if (vid.content_loc) xml += ` <video:content_loc>${escapeXml(vid.content_loc)}</video:content_loc>
|
|
110
|
+
`;
|
|
111
|
+
if (vid.player_loc) xml += ` <video:player_loc>${escapeXml(vid.player_loc)}</video:player_loc>
|
|
112
|
+
`;
|
|
113
|
+
if (vid.publication_date) {
|
|
114
|
+
const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;
|
|
115
|
+
xml += ` <video:publication_date>${vDate}</video:publication_date>
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
xml += ` </video:video>
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (entry.news) {
|
|
123
|
+
const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;
|
|
124
|
+
xml += ` <news:news>
|
|
125
|
+
`;
|
|
126
|
+
xml += ` <news:publication>
|
|
127
|
+
`;
|
|
128
|
+
xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>
|
|
129
|
+
`;
|
|
130
|
+
xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>
|
|
131
|
+
`;
|
|
132
|
+
xml += ` </news:publication>
|
|
133
|
+
`;
|
|
134
|
+
xml += ` <news:publication_date>${nDate}</news:publication_date>
|
|
135
|
+
`;
|
|
136
|
+
xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>
|
|
137
|
+
`;
|
|
138
|
+
xml += ` </news:news>
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
xml += ` </url>
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
xml += `</urlset>`;
|
|
145
|
+
return xml;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/index.ts
|
|
149
|
+
function getServerSitemapResponse(entries) {
|
|
150
|
+
const xml = generateXml(entries);
|
|
151
|
+
return new Response(xml, {
|
|
152
|
+
headers: {
|
|
153
|
+
"Content-Type": "application/xml",
|
|
154
|
+
"Cache-Control": "public, s-maxage=86400, stale-while-revalidate"
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
159
|
+
0 && (module.exports = {
|
|
160
|
+
getServerSitemapResponse
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/xml-escape.ts","../src/core/generator.ts"],"sourcesContent":["import { SitemapEntry } from './types/sitemap.js';\r\nimport { generateXml } from './core/generator.js';\r\n\r\nexport * from './types/sitemap.js';\r\n\r\n/**\r\n * Génère une réponse HTTP compatible Next.js (App Router)\r\n * * @param entries - Liste des entrées du sitemap\r\n * @returns Une instance de Response contenant le flux XML\r\n */\r\nexport function getServerSitemapResponse(entries: SitemapEntry[]): Response {\r\n const xml = generateXml(entries);\r\n\r\n return new Response(xml, {\r\n headers: {\r\n 'Content-Type': 'application/xml',\r\n 'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate',\r\n },\r\n });\r\n}","export function escapeXml(unsafe: string): string {\r\n return unsafe.replace(/[<>&\"']/g, (c) => {\r\n switch (c) {\r\n case '<': return '<';\r\n case '>': return '>';\r\n case '&': return '&';\r\n case '\"': return '"';\r\n case \"'\": return ''';\r\n default: return c;\r\n }\r\n });\r\n}","import { SitemapEntry } from '../types/sitemap.js';\r\nimport { escapeXml } from '../utils/xml-escape.js';\r\n\r\n/**\r\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\r\n */\r\nexport function generateXml(entries: SitemapEntry[]): string {\r\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\r\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\r\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\r\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\r\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\r\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\r\n\r\n for (const entry of entries) {\r\n xml += ` <url>\\n`;\r\n xml += ` <loc>${escapeXml(entry.url)}</loc>\\n`;\r\n\r\n // Support Hreflang (Internationalisation)\r\n if (entry.alternates?.length) {\r\n for (const alt of entry.alternates) {\r\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(alt.href)}\" />\\n`;\r\n }\r\n }\r\n\r\n // Métadonnées standard\r\n if (entry.lastmod) {\r\n const date = entry.lastmod instanceof Date ? entry.lastmod.toISOString() : entry.lastmod;\r\n xml += ` <lastmod>${date}</lastmod>\\n`;\r\n }\r\n\r\n if (entry.changefreq) {\r\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\r\n }\r\n\r\n if (entry.priority !== undefined) {\r\n xml += ` <priority>${entry.priority.toFixed(1)}</priority>\\n`;\r\n }\r\n\r\n // Extension Images\r\n if (entry.images?.length) {\r\n for (const img of entry.images) {\r\n xml += ` <image:image>\\n`;\r\n xml += ` <image:loc>${escapeXml(img.loc)}</image:loc>\\n`;\r\n if (img.title) xml += ` <image:title>${escapeXml(img.title)}</image:title>\\n`;\r\n if (img.caption) xml += ` <image:caption>${escapeXml(img.caption)}</image:caption>\\n`;\r\n xml += ` </image:image>\\n`;\r\n }\r\n }\r\n\r\n // Extension Vidéos\r\n if (entry.videos?.length) {\r\n for (const vid of entry.videos) {\r\n xml += ` <video:video>\\n`;\r\n xml += ` <video:thumbnail_loc>${escapeXml(vid.thumbnail_loc)}</video:thumbnail_loc>\\n`;\r\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\r\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\r\n if (vid.content_loc) xml += ` <video:content_loc>${escapeXml(vid.content_loc)}</video:content_loc>\\n`;\r\n if (vid.player_loc) xml += ` <video:player_loc>${escapeXml(vid.player_loc)}</video:player_loc>\\n`;\r\n if (vid.publication_date) {\r\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\r\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\r\n }\r\n xml += ` </video:video>\\n`;\r\n }\r\n }\r\n\r\n // Extension News\r\n if (entry.news) {\r\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\r\n xml += ` <news:news>\\n`;\r\n xml += ` <news:publication>\\n`;\r\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\r\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\r\n xml += ` </news:publication>\\n`;\r\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\r\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\r\n xml += ` </news:news>\\n`;\r\n }\r\n\r\n xml += ` </url>\\n`;\r\n }\r\n\r\n xml += `</urlset>`;\r\n return xml;\r\n}"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface pour les liens alternatifs (Hreflang / Multilingue)
|
|
3
|
+
* @see https://developers.google.com/search/docs/specialty/international/localized-versions#sitemap
|
|
4
|
+
*/
|
|
5
|
+
interface SitemapAlternate {
|
|
6
|
+
hreflang: string;
|
|
7
|
+
href: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Interface pour les images dans le sitemap
|
|
11
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps
|
|
12
|
+
*/
|
|
13
|
+
interface SitemapImage {
|
|
14
|
+
loc: string;
|
|
15
|
+
caption?: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
geoLocation?: string;
|
|
18
|
+
license?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Interface pour les vidéos dans le sitemap
|
|
22
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/video-sitemaps
|
|
23
|
+
*/
|
|
24
|
+
interface SitemapVideo {
|
|
25
|
+
thumbnail_loc: string;
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
content_loc?: string;
|
|
29
|
+
player_loc?: string;
|
|
30
|
+
duration?: number;
|
|
31
|
+
view_count?: number;
|
|
32
|
+
publication_date?: string | Date;
|
|
33
|
+
family_friendly?: 'yes' | 'no';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Interface pour Google News
|
|
37
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemaps
|
|
38
|
+
*/
|
|
39
|
+
interface SitemapNews {
|
|
40
|
+
name: string;
|
|
41
|
+
language: string;
|
|
42
|
+
publication_date: string | Date;
|
|
43
|
+
title: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Interface principale représentant une entrée du sitemap
|
|
47
|
+
*/
|
|
48
|
+
interface SitemapEntry {
|
|
49
|
+
url: string;
|
|
50
|
+
lastmod?: string | Date;
|
|
51
|
+
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
52
|
+
priority?: number;
|
|
53
|
+
images?: SitemapImage[];
|
|
54
|
+
videos?: SitemapVideo[];
|
|
55
|
+
news?: SitemapNews;
|
|
56
|
+
alternates?: SitemapAlternate[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Génère une réponse HTTP compatible Next.js (App Router)
|
|
61
|
+
* * @param entries - Liste des entrées du sitemap
|
|
62
|
+
* @returns Une instance de Response contenant le flux XML
|
|
63
|
+
*/
|
|
64
|
+
declare function getServerSitemapResponse(entries: SitemapEntry[]): Response;
|
|
65
|
+
|
|
66
|
+
export { type SitemapAlternate, type SitemapEntry, type SitemapImage, type SitemapNews, type SitemapVideo, getServerSitemapResponse };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface pour les liens alternatifs (Hreflang / Multilingue)
|
|
3
|
+
* @see https://developers.google.com/search/docs/specialty/international/localized-versions#sitemap
|
|
4
|
+
*/
|
|
5
|
+
interface SitemapAlternate {
|
|
6
|
+
hreflang: string;
|
|
7
|
+
href: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Interface pour les images dans le sitemap
|
|
11
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps
|
|
12
|
+
*/
|
|
13
|
+
interface SitemapImage {
|
|
14
|
+
loc: string;
|
|
15
|
+
caption?: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
geoLocation?: string;
|
|
18
|
+
license?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Interface pour les vidéos dans le sitemap
|
|
22
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/video-sitemaps
|
|
23
|
+
*/
|
|
24
|
+
interface SitemapVideo {
|
|
25
|
+
thumbnail_loc: string;
|
|
26
|
+
title: string;
|
|
27
|
+
description: string;
|
|
28
|
+
content_loc?: string;
|
|
29
|
+
player_loc?: string;
|
|
30
|
+
duration?: number;
|
|
31
|
+
view_count?: number;
|
|
32
|
+
publication_date?: string | Date;
|
|
33
|
+
family_friendly?: 'yes' | 'no';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Interface pour Google News
|
|
37
|
+
* @see https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemaps
|
|
38
|
+
*/
|
|
39
|
+
interface SitemapNews {
|
|
40
|
+
name: string;
|
|
41
|
+
language: string;
|
|
42
|
+
publication_date: string | Date;
|
|
43
|
+
title: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Interface principale représentant une entrée du sitemap
|
|
47
|
+
*/
|
|
48
|
+
interface SitemapEntry {
|
|
49
|
+
url: string;
|
|
50
|
+
lastmod?: string | Date;
|
|
51
|
+
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
52
|
+
priority?: number;
|
|
53
|
+
images?: SitemapImage[];
|
|
54
|
+
videos?: SitemapVideo[];
|
|
55
|
+
news?: SitemapNews;
|
|
56
|
+
alternates?: SitemapAlternate[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Génère une réponse HTTP compatible Next.js (App Router)
|
|
61
|
+
* * @param entries - Liste des entrées du sitemap
|
|
62
|
+
* @returns Une instance de Response contenant le flux XML
|
|
63
|
+
*/
|
|
64
|
+
declare function getServerSitemapResponse(entries: SitemapEntry[]): Response;
|
|
65
|
+
|
|
66
|
+
export { type SitemapAlternate, type SitemapEntry, type SitemapImage, type SitemapNews, type SitemapVideo, getServerSitemapResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/utils/xml-escape.ts
|
|
2
|
+
function escapeXml(unsafe) {
|
|
3
|
+
return unsafe.replace(/[<>&"']/g, (c) => {
|
|
4
|
+
switch (c) {
|
|
5
|
+
case "<":
|
|
6
|
+
return "<";
|
|
7
|
+
case ">":
|
|
8
|
+
return ">";
|
|
9
|
+
case "&":
|
|
10
|
+
return "&";
|
|
11
|
+
case '"':
|
|
12
|
+
return """;
|
|
13
|
+
case "'":
|
|
14
|
+
return "'";
|
|
15
|
+
default:
|
|
16
|
+
return c;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/core/generator.ts
|
|
22
|
+
function generateXml(entries) {
|
|
23
|
+
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
24
|
+
`;
|
|
25
|
+
xml += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
26
|
+
`;
|
|
27
|
+
xml += ` xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
|
28
|
+
`;
|
|
29
|
+
xml += ` xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
|
|
30
|
+
`;
|
|
31
|
+
xml += ` xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
|
|
32
|
+
`;
|
|
33
|
+
xml += ` xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
34
|
+
`;
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
xml += ` <url>
|
|
37
|
+
`;
|
|
38
|
+
xml += ` <loc>${escapeXml(entry.url)}</loc>
|
|
39
|
+
`;
|
|
40
|
+
if (entry.alternates?.length) {
|
|
41
|
+
for (const alt of entry.alternates) {
|
|
42
|
+
xml += ` <xhtml:link rel="alternate" hreflang="${escapeXml(alt.hreflang)}" href="${escapeXml(alt.href)}" />
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (entry.lastmod) {
|
|
47
|
+
const date = entry.lastmod instanceof Date ? entry.lastmod.toISOString() : entry.lastmod;
|
|
48
|
+
xml += ` <lastmod>${date}</lastmod>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
if (entry.changefreq) {
|
|
52
|
+
xml += ` <changefreq>${entry.changefreq}</changefreq>
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
if (entry.priority !== void 0) {
|
|
56
|
+
xml += ` <priority>${entry.priority.toFixed(1)}</priority>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
if (entry.images?.length) {
|
|
60
|
+
for (const img of entry.images) {
|
|
61
|
+
xml += ` <image:image>
|
|
62
|
+
`;
|
|
63
|
+
xml += ` <image:loc>${escapeXml(img.loc)}</image:loc>
|
|
64
|
+
`;
|
|
65
|
+
if (img.title) xml += ` <image:title>${escapeXml(img.title)}</image:title>
|
|
66
|
+
`;
|
|
67
|
+
if (img.caption) xml += ` <image:caption>${escapeXml(img.caption)}</image:caption>
|
|
68
|
+
`;
|
|
69
|
+
xml += ` </image:image>
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (entry.videos?.length) {
|
|
74
|
+
for (const vid of entry.videos) {
|
|
75
|
+
xml += ` <video:video>
|
|
76
|
+
`;
|
|
77
|
+
xml += ` <video:thumbnail_loc>${escapeXml(vid.thumbnail_loc)}</video:thumbnail_loc>
|
|
78
|
+
`;
|
|
79
|
+
xml += ` <video:title>${escapeXml(vid.title)}</video:title>
|
|
80
|
+
`;
|
|
81
|
+
xml += ` <video:description>${escapeXml(vid.description)}</video:description>
|
|
82
|
+
`;
|
|
83
|
+
if (vid.content_loc) xml += ` <video:content_loc>${escapeXml(vid.content_loc)}</video:content_loc>
|
|
84
|
+
`;
|
|
85
|
+
if (vid.player_loc) xml += ` <video:player_loc>${escapeXml(vid.player_loc)}</video:player_loc>
|
|
86
|
+
`;
|
|
87
|
+
if (vid.publication_date) {
|
|
88
|
+
const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;
|
|
89
|
+
xml += ` <video:publication_date>${vDate}</video:publication_date>
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
xml += ` </video:video>
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (entry.news) {
|
|
97
|
+
const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;
|
|
98
|
+
xml += ` <news:news>
|
|
99
|
+
`;
|
|
100
|
+
xml += ` <news:publication>
|
|
101
|
+
`;
|
|
102
|
+
xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>
|
|
103
|
+
`;
|
|
104
|
+
xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>
|
|
105
|
+
`;
|
|
106
|
+
xml += ` </news:publication>
|
|
107
|
+
`;
|
|
108
|
+
xml += ` <news:publication_date>${nDate}</news:publication_date>
|
|
109
|
+
`;
|
|
110
|
+
xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>
|
|
111
|
+
`;
|
|
112
|
+
xml += ` </news:news>
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
xml += ` </url>
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
xml += `</urlset>`;
|
|
119
|
+
return xml;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/index.ts
|
|
123
|
+
function getServerSitemapResponse(entries) {
|
|
124
|
+
const xml = generateXml(entries);
|
|
125
|
+
return new Response(xml, {
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "application/xml",
|
|
128
|
+
"Cache-Control": "public, s-maxage=86400, stale-while-revalidate"
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
getServerSitemapResponse
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/xml-escape.ts","../src/core/generator.ts","../src/index.ts"],"sourcesContent":["export function escapeXml(unsafe: string): string {\r\n return unsafe.replace(/[<>&\"']/g, (c) => {\r\n switch (c) {\r\n case '<': return '<';\r\n case '>': return '>';\r\n case '&': return '&';\r\n case '\"': return '"';\r\n case \"'\": return ''';\r\n default: return c;\r\n }\r\n });\r\n}","import { SitemapEntry } from '../types/sitemap.js';\r\nimport { escapeXml } from '../utils/xml-escape.js';\r\n\r\n/**\r\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\r\n */\r\nexport function generateXml(entries: SitemapEntry[]): string {\r\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\r\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\r\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\r\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\r\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\r\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\r\n\r\n for (const entry of entries) {\r\n xml += ` <url>\\n`;\r\n xml += ` <loc>${escapeXml(entry.url)}</loc>\\n`;\r\n\r\n // Support Hreflang (Internationalisation)\r\n if (entry.alternates?.length) {\r\n for (const alt of entry.alternates) {\r\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(alt.href)}\" />\\n`;\r\n }\r\n }\r\n\r\n // Métadonnées standard\r\n if (entry.lastmod) {\r\n const date = entry.lastmod instanceof Date ? entry.lastmod.toISOString() : entry.lastmod;\r\n xml += ` <lastmod>${date}</lastmod>\\n`;\r\n }\r\n\r\n if (entry.changefreq) {\r\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\r\n }\r\n\r\n if (entry.priority !== undefined) {\r\n xml += ` <priority>${entry.priority.toFixed(1)}</priority>\\n`;\r\n }\r\n\r\n // Extension Images\r\n if (entry.images?.length) {\r\n for (const img of entry.images) {\r\n xml += ` <image:image>\\n`;\r\n xml += ` <image:loc>${escapeXml(img.loc)}</image:loc>\\n`;\r\n if (img.title) xml += ` <image:title>${escapeXml(img.title)}</image:title>\\n`;\r\n if (img.caption) xml += ` <image:caption>${escapeXml(img.caption)}</image:caption>\\n`;\r\n xml += ` </image:image>\\n`;\r\n }\r\n }\r\n\r\n // Extension Vidéos\r\n if (entry.videos?.length) {\r\n for (const vid of entry.videos) {\r\n xml += ` <video:video>\\n`;\r\n xml += ` <video:thumbnail_loc>${escapeXml(vid.thumbnail_loc)}</video:thumbnail_loc>\\n`;\r\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\r\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\r\n if (vid.content_loc) xml += ` <video:content_loc>${escapeXml(vid.content_loc)}</video:content_loc>\\n`;\r\n if (vid.player_loc) xml += ` <video:player_loc>${escapeXml(vid.player_loc)}</video:player_loc>\\n`;\r\n if (vid.publication_date) {\r\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\r\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\r\n }\r\n xml += ` </video:video>\\n`;\r\n }\r\n }\r\n\r\n // Extension News\r\n if (entry.news) {\r\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\r\n xml += ` <news:news>\\n`;\r\n xml += ` <news:publication>\\n`;\r\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\r\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\r\n xml += ` </news:publication>\\n`;\r\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\r\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\r\n xml += ` </news:news>\\n`;\r\n }\r\n\r\n xml += ` </url>\\n`;\r\n }\r\n\r\n xml += `</urlset>`;\r\n return xml;\r\n}","import { SitemapEntry } from './types/sitemap.js';\r\nimport { generateXml } from './core/generator.js';\r\n\r\nexport * from './types/sitemap.js';\r\n\r\n/**\r\n * Génère une réponse HTTP compatible Next.js (App Router)\r\n * * @param entries - Liste des entrées du sitemap\r\n * @returns Une instance de Response contenant le flux XML\r\n */\r\nexport function getServerSitemapResponse(entries: SitemapEntry[]): Response {\r\n const xml = generateXml(entries);\r\n\r\n return new Response(xml, {\r\n headers: {\r\n 'Content-Type': 'application/xml',\r\n 'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate',\r\n },\r\n });\r\n}"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-advanced-sitemap",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Advanced sitemap generator for Next.js. Powerful support for Google Images, Video, News, and Hreflang (multilingual). Type-safe, zero-dependency, and built for App Router.",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "tsup src/index.ts --watch",
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"nextjs",
|
|
27
|
+
"sitemap",
|
|
28
|
+
"seo",
|
|
29
|
+
"google-images",
|
|
30
|
+
"google-video",
|
|
31
|
+
"google-news",
|
|
32
|
+
"hreflang",
|
|
33
|
+
"multilingual",
|
|
34
|
+
"typescript",
|
|
35
|
+
"app-router"
|
|
36
|
+
],
|
|
37
|
+
"author": "fomadev",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/fomadev/next-advanced-sitemap.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/fomadev/next-advanced-sitemap/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/fomadev/next-advanced-sitemap#readme"
|
|
47
|
+
}
|