@cocreate/sitemap 1.0.0 → 1.2.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/CHANGELOG.md +28 -0
- package/package.json +5 -2
- package/src/index.js +386 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.1.0...v1.2.0) (2024-08-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add sitemap to sitemapindex if not already in index ([0ff6911](https://github.com/CoCreate-app/CoCreate-sitemap/commit/0ff6911fe81eeb05295639fb984401d3e76b90ad))
|
|
7
|
+
* noindex metatag return and handling sitemap generation ([058116a](https://github.com/CoCreate-app/CoCreate-sitemap/commit/058116a34c44100bfa4ea1b20afe13fd489bc642))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* added node-html-parser ([397c590](https://github.com/CoCreate-app/CoCreate-sitemap/commit/397c5908af1149d2d60d7c431a8a0f1bd60789f4))
|
|
13
|
+
* parseHtml function to read and generate nested sitemaps using sitemap-* attributes and metatags ([64edbdb](https://github.com/CoCreate-app/CoCreate-sitemap/commit/64edbdbdcee6f64bc5080997557f9691f072381c))
|
|
14
|
+
|
|
15
|
+
# [1.1.0](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.0.0...v1.1.0) (2024-08-24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* apply host for crud ([9a9fbf0](https://github.com/CoCreate-app/CoCreate-sitemap/commit/9a9fbf0c636d6aa50a607e9fdd2cf96c29f0e1bc))
|
|
21
|
+
* check logic to auto add html files to sitemap ([6ca692b](https://github.com/CoCreate-app/CoCreate-sitemap/commit/6ca692b486ad2f26c7140349756ebc7454ce2e00))
|
|
22
|
+
* handle mainSitmap and sitmap as a file object ([99b97c3](https://github.com/CoCreate-app/CoCreate-sitemap/commit/99b97c3aa3198ea090a2efe8d9d2af5875af73ed))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* create multiple sitemaps for the various supported types ([98af2dc](https://github.com/CoCreate-app/CoCreate-sitemap/commit/98af2dc972ae15a34caaf709358d543f67828e95))
|
|
28
|
+
|
|
1
29
|
# 1.0.0 (2024-06-27)
|
|
2
30
|
|
|
3
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/sitemap",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A simple sitemap component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sitemap",
|
|
@@ -43,5 +43,8 @@
|
|
|
43
43
|
"type": "GitHub Sponsors ❤",
|
|
44
44
|
"url": "https://github.com/sponsors/CoCreate-app"
|
|
45
45
|
},
|
|
46
|
-
"main": "./src/index.js"
|
|
46
|
+
"main": "./src/index.js",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"node-html-parser": "^6.1.13"
|
|
49
|
+
}
|
|
47
50
|
}
|
package/src/index.js
CHANGED
|
@@ -20,53 +20,410 @@
|
|
|
20
20
|
// you must obtain a commercial license from CoCreate LLC.
|
|
21
21
|
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const { parse } = require("node-html-parser");
|
|
24
24
|
|
|
25
25
|
class CoCreateSitemap {
|
|
26
|
-
constructor(
|
|
27
|
-
this.
|
|
26
|
+
constructor(crud) {
|
|
27
|
+
this.crud = crud;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
async check(file, host) {
|
|
31
|
+
// Ensure the file is HTML and does not have a sitemap object yet
|
|
32
|
+
if (!file.sitemap && file['content-type'] !== 'text/html')
|
|
33
|
+
return;
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Ensure the file is public
|
|
36
|
+
if (!file.public || file.public === "false")
|
|
37
|
+
return;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
// Check if the file is HTML and contains a noindex meta tag
|
|
40
|
+
if (file['content-type'] === 'text/html'
|
|
41
|
+
&& /<meta\s+name=["']robots["']\s+content=["'][^"']*noindex[^"']*["']/i.test(file.src))
|
|
42
|
+
return;
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
// Compare the lastmod date in the sitemap with the modified.on date
|
|
45
|
+
if (file.sitemap && file.sitemap.lastmod && file.modified.on) {
|
|
46
|
+
if (new Date(file.sitemap.lastmod) >= new Date(file.modified.on))
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`URL ${urlToFind} found in sitemap at position ${position}-${endPosition}.`);
|
|
50
|
+
// Logic to update the sitemap
|
|
51
|
+
this.updateSitemap(file, host);
|
|
52
|
+
}
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<priority>1.0</priority>
|
|
54
|
-
</url>`;
|
|
54
|
+
async updateSitemap(file, host) {
|
|
55
|
+
try {
|
|
56
|
+
// TODO: need to get info such as host
|
|
57
|
+
const entry = this.createEntry(file);
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
sitemapXml = sitemapXml.slice(0, position) + modifiedUrlBlock + sitemapXml.slice(endPosition);
|
|
59
|
+
let { mainSitemap, sitemap } = await this.getSitemap(file, host);
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
if (file.pathname) {
|
|
62
|
+
// Perform regex search starting at the pathname
|
|
63
|
+
const regexPattern = `<url>\\s*<loc>.*?${file.pathname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}.*?</loc>[\\s\\S]*?</url>`;
|
|
64
|
+
const match = sitemap.src.match(new RegExp(regexPattern));
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
if (match) {
|
|
67
|
+
const position = match.index; // Start position of the <url> block
|
|
68
|
+
const endPosition = match.index + match[0].length; // End position of the <url> block
|
|
69
|
+
|
|
70
|
+
// Replace the original <url> block with the modified one
|
|
71
|
+
sitemap.src = sitemap.src.slice(0, position) + entry + sitemap.src.slice(endPosition);
|
|
72
|
+
} else {
|
|
73
|
+
sitemap.src = sitemap.src.replace('</urlset>', `${entry}</urlset>`);
|
|
74
|
+
}
|
|
63
75
|
} else {
|
|
64
|
-
|
|
76
|
+
file.sitemap.pathname = sitemap.pathname
|
|
77
|
+
sitemap.src = sitemap.src.replace('</urlset>', `${entry}</urlset>`);
|
|
65
78
|
}
|
|
79
|
+
|
|
80
|
+
this.saveSitemap(mainSitemap, host);
|
|
81
|
+
this.saveSitemap(sitemap, host);
|
|
82
|
+
this.saveSitemap(file, host);
|
|
83
|
+
|
|
84
|
+
// console.log('Sitemap updated successfully.');
|
|
66
85
|
} catch (err) {
|
|
67
86
|
console.error('Error updating sitemap:', err);
|
|
68
87
|
}
|
|
69
88
|
}
|
|
89
|
+
|
|
90
|
+
createEntry(file) {
|
|
91
|
+
file.sitemap.loc = file.pathname;
|
|
92
|
+
file.sitemap.lastmod = file.modified.on;
|
|
93
|
+
|
|
94
|
+
if (file['content-type'] === 'text/html') {
|
|
95
|
+
parseHtml(file)
|
|
96
|
+
|
|
97
|
+
if (file.sitemap.type !== 'news') {
|
|
98
|
+
if (file.sitemap.changefreq)
|
|
99
|
+
file.sitemap.changefreq = 'monthly';
|
|
100
|
+
|
|
101
|
+
if (!file.sitemap.priority) {
|
|
102
|
+
const depth = (file.pathname.match(/\//g) || []).length;
|
|
103
|
+
file.sitemap.priority = Math.max(0.1, 1.0 - (depth - 1) * 0.1).toFixed(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let entry = `\t<url>\n`;
|
|
109
|
+
|
|
110
|
+
for (const key of Object.keys(file.sitemap)) {
|
|
111
|
+
if (key === 'pathname' || key === 'type')
|
|
112
|
+
continue
|
|
113
|
+
let value = file.sitemap[key];
|
|
114
|
+
|
|
115
|
+
if (typeof value === 'object' && value !== null && !(value instanceof Date)) {
|
|
116
|
+
if (!Array.isArray(value))
|
|
117
|
+
value = [value]
|
|
118
|
+
for (let i = 0; i < value.length; i++) {
|
|
119
|
+
entry += `\t\t<${key}:${key}>\n`;
|
|
120
|
+
|
|
121
|
+
for (const nestedKey of Object.keys(value[i])) {
|
|
122
|
+
const nestedValue = value[nestedKey];
|
|
123
|
+
entry += `\t\t\t<${key}:${nestedKey}>${nestedValue}</${key}:${nestedKey}>\n`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
entry += `\t\t</${key}:${key}>\n`;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
entry += `\t\t<${key}>${value}</${key}>\n`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
entry += `\t</url>\n`;
|
|
134
|
+
|
|
135
|
+
return entry;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getSitemap(file, host) {
|
|
139
|
+
let mainSitemap = {
|
|
140
|
+
host: file.host,
|
|
141
|
+
name: 'sitemap.xml',
|
|
142
|
+
path: '/',
|
|
143
|
+
pathname: '/sitemap.xml',
|
|
144
|
+
directory: '/',
|
|
145
|
+
'content-type': 'application/xml',
|
|
146
|
+
public: true,
|
|
147
|
+
organization_id: file.organization_id
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
mainSitemap = await this.readSitemap(mainSitemap, host);
|
|
151
|
+
if (!mainSitemap.src)
|
|
152
|
+
mainSitemap.src = this.createSitemap('main')
|
|
153
|
+
|
|
154
|
+
let sitemap = {
|
|
155
|
+
host: file.host,
|
|
156
|
+
path: '/',
|
|
157
|
+
pathname: file.sitemap.pathname,
|
|
158
|
+
directory: '/',
|
|
159
|
+
'content-type': 'application/xml',
|
|
160
|
+
public: true,
|
|
161
|
+
organization_id: file.organization_id
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Update loc using pathname
|
|
165
|
+
file.sitemap.loc = `${file.pathname}`
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
// Query the database for the correct sitemap based on the loc and type
|
|
169
|
+
if (file.sitemap.pathname) {
|
|
170
|
+
sitemap = await this.readSitemap(sitemap, host);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!sitemap.src) {
|
|
174
|
+
let type = 'sitemap';
|
|
175
|
+
|
|
176
|
+
// Identify content type to determine sitemap type
|
|
177
|
+
if (file['content-type'].startsWith('image/')) {
|
|
178
|
+
type = 'image';
|
|
179
|
+
} else if (file['content-type'].startsWith('video/')) {
|
|
180
|
+
type = 'video';
|
|
181
|
+
} else if (file.sitemap.type === 'news') {
|
|
182
|
+
type = 'news';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let name = `sitemap`
|
|
186
|
+
if (type === 'image' || type === 'video' || type === 'news')
|
|
187
|
+
name = `sitemap-${type}`
|
|
188
|
+
|
|
189
|
+
// If no existing sitemap found check last index sitemap
|
|
190
|
+
let index = await this.getLastSitemapIndex(mainSitemap, name);
|
|
191
|
+
if (index) {
|
|
192
|
+
sitemap.pathname = `/${name}${index}.xml`
|
|
193
|
+
sitemap = await this.readSitemap(sitemap, host);
|
|
194
|
+
} else {
|
|
195
|
+
index = 1
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check if there's room in the last index sitemap
|
|
199
|
+
if (!this.checkSitemap(sitemap.src)) {
|
|
200
|
+
if (sitemap.src)
|
|
201
|
+
index += 1
|
|
202
|
+
else
|
|
203
|
+
sitemap.src = this.createSitemap(type);
|
|
204
|
+
|
|
205
|
+
sitemap.name = `${name}${index}.xml`
|
|
206
|
+
sitemap.pathname = `/${name}${index}.xml`
|
|
207
|
+
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Create the regex pattern to match the <sitemap> block containing the specific <loc> for the pathname
|
|
213
|
+
const regexPattern = `<sitemap>\\s*<loc>[^<]*${sitemap.pathname}[^<]*</loc>[\\s\\S]*?</sitemap>`;
|
|
214
|
+
|
|
215
|
+
// Execute the regex match against the sitemap index source
|
|
216
|
+
const match = mainSitemap.src.match(new RegExp(regexPattern));
|
|
217
|
+
|
|
218
|
+
// Check if a match is found
|
|
219
|
+
if (!match) {
|
|
220
|
+
//TODO: if sitemap found but not in index should we add to sitemap pathname to index or should we check the sitmap for the next index available see if room add or create new index.
|
|
221
|
+
const indexEntry = `\t<sitemap>\n\t\t<loc>{{$host}}${sitemap.pathname}</loc>\n</sitemap>`;
|
|
222
|
+
mainSitemap.src = mainSitemap.src.replace('</sitemapindex>', `${indexEntry}\n</sitemapindex>`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { mainSitemap, sitemap }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async readSitemap(file, host) {
|
|
229
|
+
let data = {
|
|
230
|
+
method: 'object.read',
|
|
231
|
+
host: host,
|
|
232
|
+
array: 'files',
|
|
233
|
+
$filter: {
|
|
234
|
+
query: {
|
|
235
|
+
host: { $in: [host, '*'] },
|
|
236
|
+
pathname: file.pathname
|
|
237
|
+
},
|
|
238
|
+
limit: 1
|
|
239
|
+
},
|
|
240
|
+
organization_id: file.organization_id
|
|
241
|
+
}
|
|
242
|
+
data = await this.crud.send(data)
|
|
243
|
+
if (data.object && data.object.length)
|
|
244
|
+
return data.object[0]
|
|
245
|
+
else
|
|
246
|
+
return file
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
createSitemap(type) {
|
|
250
|
+
const xmlDeclaration = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
|
251
|
+
const sitemapNamespace = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
|
|
252
|
+
|
|
253
|
+
if (type === 'main') {
|
|
254
|
+
return `${xmlDeclaration}<sitemapindex ${sitemapNamespace}>\n</sitemapindex>`;
|
|
255
|
+
} else {
|
|
256
|
+
const imageNamespace = 'xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"';
|
|
257
|
+
const videoNamespace = 'xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
|
|
258
|
+
const newsNamespace = 'xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"';
|
|
259
|
+
|
|
260
|
+
if (type === 'image') {
|
|
261
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${imageNamespace}>\n</urlset>`;
|
|
262
|
+
} else if (type === 'video') {
|
|
263
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${videoNamespace}>\n</urlset>`;
|
|
264
|
+
} else { // For 'news' type or any other types
|
|
265
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${newsNamespace} ${imageNamespace} ${videoNamespace}>\n</urlset>`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async saveSitemap(file, host) {
|
|
271
|
+
let data = {
|
|
272
|
+
method: 'object.update',
|
|
273
|
+
host: host,
|
|
274
|
+
array: 'files',
|
|
275
|
+
object: file,
|
|
276
|
+
upsert: true,
|
|
277
|
+
organization_id: file.organization_id
|
|
278
|
+
}
|
|
279
|
+
if (!file._id)
|
|
280
|
+
data.$filter = {
|
|
281
|
+
query: {
|
|
282
|
+
host: { $in: [host, '*'] },
|
|
283
|
+
pathname: file.pathname
|
|
284
|
+
},
|
|
285
|
+
limit: 1
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
data = await this.crud.send(data)
|
|
289
|
+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async getLastSitemapIndex(mainSitemap, filename) {
|
|
293
|
+
try {
|
|
294
|
+
// Use regex to match all sitemap entries for the given type
|
|
295
|
+
const regex = new RegExp(`\\/${filename}(\\d*)\\.xml<\\/loc>`, 'g');
|
|
296
|
+
const matches = mainSitemap.src.match(regex);
|
|
297
|
+
|
|
298
|
+
return matches ? matches.length : null;
|
|
299
|
+
} catch (err) {
|
|
300
|
+
console.error(`Error determining next sitemap index for ${filename}:`, err);
|
|
301
|
+
return null; // Or some default value or throw an error
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
checkSitemap(sitemap) {
|
|
306
|
+
try {
|
|
307
|
+
if (!sitemap)
|
|
308
|
+
return false
|
|
309
|
+
|
|
310
|
+
// Count the number of <url> entries
|
|
311
|
+
const urlCount = (sitemap.match(/<url>/g) || []).length;
|
|
312
|
+
if (urlCount >= 50000)
|
|
313
|
+
return false;
|
|
314
|
+
|
|
315
|
+
// Get the size of the sitemap string in bytes
|
|
316
|
+
const fileSizeInBytes = Buffer.byteLength(sitemap, 'utf8');
|
|
317
|
+
const fileSizeInMB = fileSizeInBytes / (1024 * 1024);
|
|
318
|
+
|
|
319
|
+
// console.log(`Sitemap has ${urlCount} entries and is ${fileSizeInMB.toFixed(2)} MB.`);
|
|
320
|
+
|
|
321
|
+
// Check if the file size exceeds either the 50MB limit or the MongoDB 16MB limit
|
|
322
|
+
if (fileSizeInMB >= 50 || fileSizeInMB >= 15)
|
|
323
|
+
return false;
|
|
324
|
+
|
|
325
|
+
return true;
|
|
326
|
+
} catch (err) {
|
|
327
|
+
console.error('Error checking sitemap file:', err);
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
parseHtml(file) {
|
|
333
|
+
const dom = parse(html);
|
|
334
|
+
const entries = dom.querySelectorAll('[sitemap="true"]');
|
|
335
|
+
|
|
336
|
+
let types = ['image', 'video', 'news']
|
|
337
|
+
|
|
338
|
+
const previousEntries = {}
|
|
339
|
+
for (let i = 0; i < types.length; i++) {
|
|
340
|
+
if (!file.sitemap[types[i]])
|
|
341
|
+
continue
|
|
342
|
+
if (Array.isArray(file.sitemap[types[i]]))
|
|
343
|
+
previousEntries[types[i]] = file.sitemap[types[i]]
|
|
344
|
+
else
|
|
345
|
+
previousEntries[types[i]] = [file.sitemap[types[i]]]
|
|
346
|
+
|
|
347
|
+
delete file.sitemap[types[i]]
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (let i = 0; i < entries.length; i++) {
|
|
351
|
+
let type = '', query = '';
|
|
352
|
+
let existingObject
|
|
353
|
+
let entryObject = {};
|
|
354
|
+
|
|
355
|
+
if (entries[i].tagName === 'IMG') { // Corrected to 'IMG' for images
|
|
356
|
+
type = 'image';
|
|
357
|
+
query = 'loc'
|
|
358
|
+
entryObject.loc = entries[i].src;
|
|
359
|
+
entryObject.title = entries[i].getAttribute('sitemap-title') || entries[i].getAttribute('title') || entries[i].getAttribute('alt');
|
|
360
|
+
entryObject.caption = entries[i].getAttribute('sitemap-caption') || entries[i].getAttribute('alt') || entryObject.title;
|
|
361
|
+
entryObject.geo_location = entries[i].getAttribute('sitemap-geo-location');
|
|
362
|
+
} else if (entries[i].tagName === 'VIDEO') {
|
|
363
|
+
type = 'video';
|
|
364
|
+
query = 'content_loc'
|
|
365
|
+
entryObject.content_loc = entries[i].src;
|
|
366
|
+
entryObject.title = entries[i].getAttribute('sitemap-title') || entries[i].getAttribute('title');
|
|
367
|
+
entryObject.description = entries[i].getAttribute('description'); // 'description' if available
|
|
368
|
+
entryObject.thumbnail_loc = entries[i].getAttribute('sitemap-thumbnail') || entries[i].getAttribute('poster');
|
|
369
|
+
entryObject.duration = entries[i].getAttribute('sitemap-duration');
|
|
370
|
+
} else {
|
|
371
|
+
type = 'news';
|
|
372
|
+
file.sitemap.type = 'news';
|
|
373
|
+
query = 'title'
|
|
374
|
+
entryObject.title = entries[i].getAttribute('sitemap-title');
|
|
375
|
+
if (!entryObject.title) {
|
|
376
|
+
const title = dom.querySelector('title');
|
|
377
|
+
entryObject.title = title ? title.text : '';
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
entryObject.publication = {
|
|
381
|
+
name: entries[i].getAttribute('sitemap-publication-name'), // Use proper attribute
|
|
382
|
+
language: entries[i].getAttribute('sitemap-publication-language') // Use proper attribute
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
if (!entryObject.publication.language) {
|
|
386
|
+
// Fallback to HTML lang attribute
|
|
387
|
+
const htmlElement = dom.querySelector('html');
|
|
388
|
+
entryObject.publication.language = htmlElement ? htmlElement.getAttribute('lang') : null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
entryObject.publication_date = entries[i].getAttribute('sitemap-publication-date') || file.modified.on;
|
|
392
|
+
|
|
393
|
+
entryObject.keywords = entries[i].getAttribute('sitemap-keywords');
|
|
394
|
+
if (!entryObject.keywords) {
|
|
395
|
+
const keywords = dom.querySelector('meta[name="keywords"]');
|
|
396
|
+
entryObject.keywords = keywords ? keywords.getAttribute('content') : '';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
entryObject.genres = entries[i].getAttribute('sitemap-genres');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (previousEntries[type]) {
|
|
403
|
+
existingObject = previousEntries[type].find(item => item[query] === entryObject[query]);
|
|
404
|
+
entryObject = { ...existingObject, ...entryObject }
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
Object.keys(entryObject).forEach(key => {
|
|
408
|
+
if (!entryObject[key])
|
|
409
|
+
delete entryObject[key]
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
if (!file.sitemap[type])
|
|
413
|
+
file.sitemap[type] = []
|
|
414
|
+
|
|
415
|
+
file.sitemap[type].push(entryObject)
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (file.sitemap.type !== 'news') {
|
|
420
|
+
const priorityMeta = dom.querySelector('meta[name="sitemap-priority"]');
|
|
421
|
+
const changefreqMeta = dom.querySelector('meta[name="sitemap-changefreq"]');
|
|
422
|
+
file.sitemap.priority = priorityMeta ? priorityMeta.getAttribute('content') : file.sitemap.priority; // Default priority if not specified
|
|
423
|
+
file.sitemap.changefreq = changefreqMeta ? changefreqMeta.getAttribute('content') : file.sitemap.changefreq; // Default changefreq if not specified
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
}
|
|
70
427
|
}
|
|
71
428
|
|
|
72
429
|
module.exports = CoCreateSitemap;
|