vite-plugin-sitemap-ts 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,6 +41,7 @@ sitemap({
41
41
  hostname: 'https://example.com',
42
42
  })
43
43
  ```
44
+
44
45
  *Output:*
45
46
  ```xml
46
47
  <?xml version="1.0" encoding="UTF-8"?>
@@ -62,6 +63,7 @@ sitemap({
62
63
  routes: ['/about'],
63
64
  })
64
65
  ```
66
+
65
67
  *Output:*
66
68
  ```xml
67
69
  <?xml version="1.0" encoding="UTF-8"?>
@@ -73,13 +75,88 @@ sitemap({
73
75
  </urlset>
74
76
  ```
75
77
 
78
+ ### With route objects:
79
+
80
+ Route objects (aka. `SitemapEntry` objects) allow for full control over "loc", "lastmod", "changefreq", "priority" and "hreflang".
81
+
82
+ ```ts
83
+ sitemap({
84
+ hostname: 'https://example.com',
85
+ routes: [
86
+ '/',
87
+ '/about',
88
+ {
89
+ loc: '/blog',
90
+ lastmod: '2026-01-01',
91
+ changefreq: 'weekly',
92
+ priority: 0.8
93
+ },
94
+ ],
95
+ })
96
+ ```
97
+
98
+ *Output:*
99
+ ```xml
100
+ <?xml version="1.0" encoding="UTF-8"?>
101
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
102
+ <url>
103
+ <loc>https://example.com/</loc>
104
+ <lastmod>2026-03-14T20:31:12.450Z</lastmod>
105
+ </url>
106
+ <url>
107
+ <loc>https://example.com/about</loc>
108
+ <lastmod>2026-03-14T20:31:12.450Z</lastmod>
109
+ </url>
110
+ <url>
111
+ <loc>https://example.com/blog</loc>
112
+ <lastmod>2026-01-01</lastmod>
113
+ <changefreq>weekly</changefreq>
114
+ <priority>0.8</priority>
115
+ </url>
116
+ </urlset>
117
+ ```
118
+
119
+ ### With hreflang (i18n support):
120
+
121
+ Use the `hreflang` property on a `SitemapEntry` to define alternate language versions of a page.
122
+
123
+ ```ts
124
+ sitemap({
125
+ hostname: 'https://example.com',
126
+ routes: [
127
+ {
128
+ loc: '/about',
129
+ hreflang: [
130
+ { lang: 'en', href: 'https://example.com/about' },
131
+ { lang: 'de', href: 'https://example.com/de/ueber' },
132
+ { lang: 'x-default', href: 'https://example.com/about' },
133
+ ],
134
+ },
135
+ ],
136
+ })
137
+ ```
138
+
139
+ *Output:*
140
+ ```xml
141
+ <?xml version="1.0" encoding="UTF-8"?>
142
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
143
+ <url>
144
+ <loc>https://example.com/about</loc>
145
+ <lastmod>2026-03-14T21:02:53.166Z</lastmod>
146
+ <xhtml:link rel="alternate" hreflang="en" href="https://example.com/about" />
147
+ <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/ueber" />
148
+ <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about" />
149
+ </url>
150
+ </urlset>
151
+ ```
152
+
76
153
  ## Options
77
154
 
78
155
  The `hostname` option is required. All other options are optional.
79
156
 
80
- | Option | Type | Default | Description |
81
- |----------|------------|---------|----------------------------------------------------------------------|
82
- | hostname | *string* | - | The hostname of the site, used to build the full URLs in the sitemap |
83
- | enabled | *boolean* | `true` | Toggle the plugin on or off |
84
- | routes | *string[]* | `['/']` | An array of routes to include in the sitemap |
157
+ | Option | Type | Default | Description |
158
+ |----------|---------------------------------------------------|---------|----------------------------------------------------------------------|
159
+ | hostname | *string* | - | The hostname of the site, used to build the full URLs in the sitemap |
160
+ | enabled | *boolean* | `true` | Toggle the plugin on or off |
161
+ | routes | *(string \| [SitemapEntry](./src/types.ts#L8))[]* | `['/']` | An array of routes to include in the sitemap |
85
162
 
package/dist/index.cjs CHANGED
@@ -42,8 +42,8 @@ var import_node_path = __toESM(require("path"), 1);
42
42
  var SPACER = " ";
43
43
  var DSPACER = SPACER + SPACER;
44
44
  var xmlHeader = `<?xml version="1.0" encoding="UTF-8"?>`;
45
- var xmlSchema = (content) => {
46
- return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
45
+ var xmlSchema = (content, hasHreflang) => {
46
+ return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"${hasHreflang ? ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' : ""}>
47
47
  ${content}
48
48
  </urlset>`;
49
49
  };
@@ -66,6 +66,7 @@ var escapeXml = (value) => {
66
66
  });
67
67
  };
68
68
  var generateSitemap = (entries) => {
69
+ let hasHreflang = false;
69
70
  const urls = entries.map((entry) => {
70
71
  let sitemapEntry = `${DSPACER}<loc>${escapeXml(entry.loc)}</loc>`;
71
72
  if (entry.lastmod) {
@@ -80,20 +81,34 @@ ${DSPACER}<changefreq>${escapeXml(entry.changefreq)}</changefreq>`;
80
81
  sitemapEntry += `
81
82
  ${DSPACER}<priority>${escapeXml(entry.priority)}</priority>`;
82
83
  }
84
+ if (entry.hreflang?.length) {
85
+ hasHreflang = true;
86
+ entry.hreflang.forEach((alt) => {
87
+ sitemapEntry += `
88
+ ${DSPACER}<xhtml:link rel="alternate" hreflang="${escapeXml(alt.lang)}" href="${escapeXml(alt.href)}" />`;
89
+ });
90
+ }
83
91
  return `${SPACER}<url>
84
92
  ${sitemapEntry}
85
93
  ${SPACER}</url>`;
86
94
  }).join("\n");
87
95
  return `${xmlHeader}
88
- ${xmlSchema(urls)}`;
96
+ ${xmlSchema(urls, hasHreflang)}`;
89
97
  };
90
98
  var buildSitemapEntries = (options) => {
91
99
  const host = options.hostname.replace(/\/$/, "");
92
100
  const lastmod = (/* @__PURE__ */ new Date()).toISOString();
93
101
  return options.routes.map((route) => {
102
+ if (typeof route === "string") {
103
+ return {
104
+ loc: `${host}/${route.replace(/^\/+/, "")}`,
105
+ lastmod
106
+ };
107
+ }
94
108
  return {
95
- loc: `${host}/${route.replace(/^\/+/, "")}`,
96
- lastmod
109
+ ...route,
110
+ loc: `${host}/${route.loc.replace(/^\/+/, "")}`,
111
+ lastmod: route.lastmod ?? lastmod
97
112
  };
98
113
  });
99
114
  };
package/dist/index.d.cts CHANGED
@@ -1,11 +1,16 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
4
+ type HrefLangEntry = {
5
+ lang: string;
6
+ href: string;
7
+ };
4
8
  type SitemapEntry = {
5
9
  loc: string;
6
10
  lastmod?: string;
7
11
  priority?: number;
8
12
  changefreq?: ChangeFreq;
13
+ hreflang?: HrefLangEntry[];
9
14
  };
10
15
  type Options = {
11
16
  /**
@@ -21,13 +26,25 @@ type Options = {
21
26
  */
22
27
  hostname: string;
23
28
  /**
24
- * An array of routes to include in the sitemap.
29
+ * The routes to include in the sitemap. Pass either strings or `SitemapEntry` objects
30
+ * for full control over "loc", "lastmod", "changefreq", "priority" and "hreflang".
25
31
  *
26
32
  * **Default: `['/']`**
33
+ *
34
+ * ---
35
+ *
36
+ * Example:
37
+ * ```ts
38
+ * routes: [
39
+ * '/',
40
+ * '/about',
41
+ * { loc: '/blog', lastmod: '2026-01-01', changefreq: 'weekly', priority: 0.8 },
42
+ * ]
43
+ * ```
27
44
  */
28
- routes?: string[];
45
+ routes?: (string | SitemapEntry)[];
29
46
  };
30
47
 
31
48
  declare function sitemap(options: Options): Plugin;
32
49
 
33
- export { type ChangeFreq, type Options, type SitemapEntry, sitemap };
50
+ export { type ChangeFreq, type HrefLangEntry, type Options, type SitemapEntry, sitemap };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
4
+ type HrefLangEntry = {
5
+ lang: string;
6
+ href: string;
7
+ };
4
8
  type SitemapEntry = {
5
9
  loc: string;
6
10
  lastmod?: string;
7
11
  priority?: number;
8
12
  changefreq?: ChangeFreq;
13
+ hreflang?: HrefLangEntry[];
9
14
  };
10
15
  type Options = {
11
16
  /**
@@ -21,13 +26,25 @@ type Options = {
21
26
  */
22
27
  hostname: string;
23
28
  /**
24
- * An array of routes to include in the sitemap.
29
+ * The routes to include in the sitemap. Pass either strings or `SitemapEntry` objects
30
+ * for full control over "loc", "lastmod", "changefreq", "priority" and "hreflang".
25
31
  *
26
32
  * **Default: `['/']`**
33
+ *
34
+ * ---
35
+ *
36
+ * Example:
37
+ * ```ts
38
+ * routes: [
39
+ * '/',
40
+ * '/about',
41
+ * { loc: '/blog', lastmod: '2026-01-01', changefreq: 'weekly', priority: 0.8 },
42
+ * ]
43
+ * ```
27
44
  */
28
- routes?: string[];
45
+ routes?: (string | SitemapEntry)[];
29
46
  };
30
47
 
31
48
  declare function sitemap(options: Options): Plugin;
32
49
 
33
- export { type ChangeFreq, type Options, type SitemapEntry, sitemap };
50
+ export { type ChangeFreq, type HrefLangEntry, type Options, type SitemapEntry, sitemap };
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ import path from "path";
6
6
  var SPACER = " ";
7
7
  var DSPACER = SPACER + SPACER;
8
8
  var xmlHeader = `<?xml version="1.0" encoding="UTF-8"?>`;
9
- var xmlSchema = (content) => {
10
- return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
9
+ var xmlSchema = (content, hasHreflang) => {
10
+ return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"${hasHreflang ? ' xmlns:xhtml="http://www.w3.org/1999/xhtml"' : ""}>
11
11
  ${content}
12
12
  </urlset>`;
13
13
  };
@@ -30,6 +30,7 @@ var escapeXml = (value) => {
30
30
  });
31
31
  };
32
32
  var generateSitemap = (entries) => {
33
+ let hasHreflang = false;
33
34
  const urls = entries.map((entry) => {
34
35
  let sitemapEntry = `${DSPACER}<loc>${escapeXml(entry.loc)}</loc>`;
35
36
  if (entry.lastmod) {
@@ -44,20 +45,34 @@ ${DSPACER}<changefreq>${escapeXml(entry.changefreq)}</changefreq>`;
44
45
  sitemapEntry += `
45
46
  ${DSPACER}<priority>${escapeXml(entry.priority)}</priority>`;
46
47
  }
48
+ if (entry.hreflang?.length) {
49
+ hasHreflang = true;
50
+ entry.hreflang.forEach((alt) => {
51
+ sitemapEntry += `
52
+ ${DSPACER}<xhtml:link rel="alternate" hreflang="${escapeXml(alt.lang)}" href="${escapeXml(alt.href)}" />`;
53
+ });
54
+ }
47
55
  return `${SPACER}<url>
48
56
  ${sitemapEntry}
49
57
  ${SPACER}</url>`;
50
58
  }).join("\n");
51
59
  return `${xmlHeader}
52
- ${xmlSchema(urls)}`;
60
+ ${xmlSchema(urls, hasHreflang)}`;
53
61
  };
54
62
  var buildSitemapEntries = (options) => {
55
63
  const host = options.hostname.replace(/\/$/, "");
56
64
  const lastmod = (/* @__PURE__ */ new Date()).toISOString();
57
65
  return options.routes.map((route) => {
66
+ if (typeof route === "string") {
67
+ return {
68
+ loc: `${host}/${route.replace(/^\/+/, "")}`,
69
+ lastmod
70
+ };
71
+ }
58
72
  return {
59
- loc: `${host}/${route.replace(/^\/+/, "")}`,
60
- lastmod
73
+ ...route,
74
+ loc: `${host}/${route.loc.replace(/^\/+/, "")}`,
75
+ lastmod: route.lastmod ?? lastmod
61
76
  };
62
77
  });
63
78
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-sitemap-ts",
3
- "version": "1.1.3",
3
+ "version": "1.3.0",
4
4
  "description": "Vite plugin to generate sitemap.xml. Works in dev mode.",
5
5
  "author": "Cornelius Weidmann <cornelius@kyco.io> (https://kyco.io)",
6
6
  "license": "MIT",