@cocreate/sitemap 1.4.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/automated.yml +25 -38
- package/CHANGELOG.md +29 -0
- package/package.json +3 -11
- package/release.config.js +12 -4
- package/src/index.js +450 -399
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
name: Automated Workflow
|
|
2
|
+
|
|
2
3
|
on:
|
|
3
4
|
push:
|
|
4
5
|
branches:
|
|
5
6
|
- master
|
|
7
|
+
|
|
6
8
|
jobs:
|
|
7
9
|
about:
|
|
8
10
|
runs-on: ubuntu-latest
|
|
@@ -18,52 +20,37 @@ jobs:
|
|
|
18
20
|
with:
|
|
19
21
|
direction: overwrite-github
|
|
20
22
|
githubToken: "${{ secrets.GITHUB }}"
|
|
23
|
+
|
|
21
24
|
release:
|
|
22
25
|
runs-on: ubuntu-latest
|
|
23
26
|
steps:
|
|
24
27
|
- name: Checkout
|
|
25
|
-
uses: actions/checkout@
|
|
26
|
-
- name: Setup Node.js
|
|
27
|
-
uses: actions/setup-node@v3
|
|
28
|
+
uses: actions/checkout@v4
|
|
28
29
|
with:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
fetch-depth: 0 # Required so semantic-release can trace git tags/history
|
|
31
|
+
|
|
32
|
+
- name: Setup Node.js
|
|
33
|
+
uses: actions/setup-node@v4
|
|
33
34
|
with:
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
node-version: 22
|
|
36
|
+
|
|
37
|
+
- name: Install Semantic Release & Plugins
|
|
38
|
+
# Installs semantic-release and its plugins on the runner
|
|
39
|
+
run: |
|
|
40
|
+
npm install -g semantic-release \
|
|
41
|
+
@semantic-release/changelog \
|
|
42
|
+
@semantic-release/npm \
|
|
43
|
+
@semantic-release/github \
|
|
36
44
|
@semantic-release/git
|
|
37
|
-
|
|
45
|
+
|
|
46
|
+
- name: Run Semantic Release (Native)
|
|
47
|
+
id: semantic
|
|
48
|
+
# This will automatically pick up your export default config file in the repository root
|
|
49
|
+
run: npx semantic-release
|
|
38
50
|
env:
|
|
39
|
-
GITHUB_TOKEN: "${{ secrets.
|
|
51
|
+
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
40
52
|
NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
|
|
53
|
+
|
|
41
54
|
outputs:
|
|
42
55
|
new_release_published: "${{ steps.semantic.outputs.new_release_published }}"
|
|
43
|
-
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
|
44
|
-
upload:
|
|
45
|
-
runs-on: ubuntu-latest
|
|
46
|
-
needs: release
|
|
47
|
-
if: needs.release.outputs.new_release_published == 'true'
|
|
48
|
-
env:
|
|
49
|
-
VERSION: "${{ needs.release.outputs.new_release_version }}"
|
|
50
|
-
steps:
|
|
51
|
-
- name: Checkout
|
|
52
|
-
uses: actions/checkout@v3
|
|
53
|
-
- name: Setup Node.js
|
|
54
|
-
uses: actions/setup-node@v3
|
|
55
|
-
with:
|
|
56
|
-
node-version: 16
|
|
57
|
-
- name: Set npm registry auth
|
|
58
|
-
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
|
59
|
-
- name: Install dependencies
|
|
60
|
-
run: yarn install
|
|
61
|
-
|
|
62
|
-
- name: Set Environment Variables
|
|
63
|
-
run: |
|
|
64
|
-
echo "organization_id=${{ secrets.COCREATE_ORGANIZATION_ID }}" >> $GITHUB_ENV
|
|
65
|
-
echo "key=${{ secrets.COCREATE_KEY }}" >> $GITHUB_ENV
|
|
66
|
-
echo "host=${{ secrets.COCREATE_HOST }}" >> $GITHUB_ENV
|
|
67
|
-
- name: CoCreate Upload
|
|
68
|
-
run: coc upload
|
|
69
|
-
|
|
56
|
+
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
# [1.6.0](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.5.0...v1.6.0) (2026-07-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* update automated workflow and release configuration for improved semantic release handling ([fd3183f](https://github.com/CoCreate-app/CoCreate-sitemap/commit/fd3183f36440cdc6ef003611161bcec483c094f7))
|
|
7
|
+
|
|
8
|
+
# [1.5.0](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.4.2...v1.5.0) (2026-07-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* removed post install ([65d5ea3](https://github.com/CoCreate-app/CoCreate-sitemap/commit/65d5ea331baf439992fab9c1dce5a95d7210e32e))
|
|
14
|
+
* semantic version handling. Reorganize .gitignore for improved clarity and structure ([63fa105](https://github.com/CoCreate-app/CoCreate-sitemap/commit/63fa105fe480103454c88f23d210585053190179))
|
|
15
|
+
* update .gitignore to include package-lock.json and pnpm-lock.yaml ([9bab5d7](https://github.com/CoCreate-app/CoCreate-sitemap/commit/9bab5d740c465515755a5daac62a55c697375b3d))
|
|
16
|
+
* update module export to ES6 syntax in release.config.js ([4096874](https://github.com/CoCreate-app/CoCreate-sitemap/commit/409687476de524c2bc1c88882031a8c43547c0e3))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* update package.json and refactor index.js to use ES modules ([a951277](https://github.com/CoCreate-app/CoCreate-sitemap/commit/a9512776ddd5f74bf9f9aab0ce947b80b210f6b7))
|
|
22
|
+
|
|
23
|
+
## [1.4.2](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.4.1...v1.4.2) (2025-09-01)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* added ToDo comment for future implementation ([b146feb](https://github.com/CoCreate-app/CoCreate-sitemap/commit/b146feb30816b49455143ee6adc5a0395b4f37de))
|
|
29
|
+
|
|
1
30
|
## [1.4.1](https://github.com/CoCreate-app/CoCreate-sitemap/compare/v1.4.0...v1.4.1) (2025-05-01)
|
|
2
31
|
|
|
3
32
|
|
package/package.json
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/sitemap",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
7
|
-
"cocreate",
|
|
8
|
-
"low-code-framework",
|
|
9
|
-
"no-code-framework",
|
|
10
|
-
"cocreatejs",
|
|
11
|
-
"cocreatejs-component",
|
|
12
|
-
"cocreate-framework",
|
|
13
|
-
"no-code",
|
|
14
7
|
"low-code",
|
|
15
|
-
"collaborative-framework",
|
|
16
8
|
"realtime",
|
|
17
9
|
"realtime-framework",
|
|
18
10
|
"collaboration",
|
|
@@ -26,8 +18,7 @@
|
|
|
26
18
|
"scripts": {
|
|
27
19
|
"start": "npx webpack --config webpack.config.js",
|
|
28
20
|
"build": "npx webpack --mode=production --config webpack.config.js",
|
|
29
|
-
"dev": "npx webpack --config webpack.config.js --watch"
|
|
30
|
-
"postinstall": "node -e \"const { execSync } = require('child_process'); try { execSync('coc --version', { stdio: 'ignore' }); } catch (error) { try { execSync('npm install -g @cocreate/cli', { stdio: 'inherit' }); console.log('Installed \"@cocreate/cli\" globally.'); } catch (error) { console.error('Failed to install \"@cocreate/cli\" globally:', error); } }\""
|
|
21
|
+
"dev": "npx webpack --config webpack.config.js --watch"
|
|
31
22
|
},
|
|
32
23
|
"repository": {
|
|
33
24
|
"type": "git",
|
|
@@ -43,6 +34,7 @@
|
|
|
43
34
|
"type": "GitHub Sponsors ❤",
|
|
44
35
|
"url": "https://github.com/sponsors/CoCreate-app"
|
|
45
36
|
},
|
|
37
|
+
"type": "module",
|
|
46
38
|
"main": "./src/index.js",
|
|
47
39
|
"dependencies": {
|
|
48
40
|
"node-html-parser": "^6.1.13"
|
package/release.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default {
|
|
2
2
|
dryRun: false,
|
|
3
3
|
branches: ["master"],
|
|
4
4
|
plugins: [
|
|
@@ -10,12 +10,20 @@ module.exports = {
|
|
|
10
10
|
changelogFile: "CHANGELOG.md",
|
|
11
11
|
},
|
|
12
12
|
],
|
|
13
|
-
"@semantic-release/npm",
|
|
14
|
-
"@semantic-release/github",
|
|
13
|
+
"@semantic-release/npm",
|
|
15
14
|
[
|
|
16
|
-
"@semantic-release/
|
|
15
|
+
"@semantic-release/github",
|
|
17
16
|
{
|
|
17
|
+
successComment: false,
|
|
18
|
+
failTitle: false,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"@semantic-release/git",
|
|
23
|
+
{
|
|
24
|
+
// Only stage and commit the changelog and package.json
|
|
18
25
|
assets: ["CHANGELOG.md", "package.json"],
|
|
26
|
+
message: "chore(release): ${nextRelease.version} [skip ci]",
|
|
19
27
|
},
|
|
20
28
|
],
|
|
21
29
|
],
|
package/src/index.js
CHANGED
|
@@ -1,479 +1,530 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// Ensure the file is HTML and does not have a sitemap object yet
|
|
32
|
-
if (!file.sitemap && file['content-type'] !== 'text/html')
|
|
1
|
+
import { parse } from "node-html-parser";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clean, stateless functional entry point to check and verify a file against the sitemap.
|
|
5
|
+
* All dependencies are passed explicitly via the parameters object.
|
|
6
|
+
* * @param {Object} options
|
|
7
|
+
* @param {Object} options.file - The file object being verified
|
|
8
|
+
* @param {string} options.host - The active website hostname
|
|
9
|
+
* @param {Object} options.crud - Crud database instance
|
|
10
|
+
*/
|
|
11
|
+
export async function check({ file, host, crud }) {
|
|
12
|
+
// Ensure the file is HTML and does not have a sitemap object yet
|
|
13
|
+
if (!file.sitemap && file["content-type"] !== "text/html") return;
|
|
14
|
+
|
|
15
|
+
// Ensure the file is public and is not sitemap false
|
|
16
|
+
if (
|
|
17
|
+
file.sitemap === false ||
|
|
18
|
+
file.sitemap === "false" ||
|
|
19
|
+
!file.public ||
|
|
20
|
+
file.public === "false"
|
|
21
|
+
)
|
|
22
|
+
return;
|
|
23
|
+
|
|
24
|
+
// Check if the file is HTML and contains a noindex meta or title tag
|
|
25
|
+
if (file["content-type"] === "text/html") {
|
|
26
|
+
if (
|
|
27
|
+
/<meta\s+name=["']robots["']\s+content=["'][^"']*noindex[^"']*["']/i.test(
|
|
28
|
+
file.src
|
|
29
|
+
)
|
|
30
|
+
)
|
|
33
31
|
return;
|
|
32
|
+
if (!/<title[^>]*>[\s\S]*?<\/title>/i.test(file.src)) return;
|
|
33
|
+
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Compare the lastmod date in the sitemap with the modified.on date
|
|
36
|
+
if (file.sitemap && file.sitemap.lastmod && file.modified?.on) {
|
|
37
|
+
if (new Date(file.sitemap.lastmod) >= new Date(file.modified.on))
|
|
37
38
|
return;
|
|
39
|
+
}
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return;
|
|
43
|
-
if (!(/<title[^>]*>[\s\S]*?<\/title>/i.test(file.src)))
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Compare the lastmod date in the sitemap with the modified.on date
|
|
48
|
-
if (file.sitemap && file.sitemap.lastmod && file.modified.on) {
|
|
49
|
-
if (new Date(file.sitemap.lastmod) >= new Date(file.modified.on))
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
41
|
+
// Logic to update the sitemap asynchronously
|
|
42
|
+
await updateSitemap({ file, host, crud });
|
|
43
|
+
}
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
// ==========================================
|
|
46
|
+
// Stateless Private Helper Functions
|
|
47
|
+
// ==========================================
|
|
56
48
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
file.sitemap = {}
|
|
49
|
+
async function updateSitemap({ file, host, crud }) {
|
|
50
|
+
try {
|
|
51
|
+
if (!file.sitemap) file.sitemap = {};
|
|
61
52
|
|
|
62
|
-
|
|
53
|
+
const entry = createEntry(file);
|
|
63
54
|
|
|
64
|
-
|
|
55
|
+
let { mainSitemap, sitemap } = await getSitemap({ file, host, crud });
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
if (file.pathname) {
|
|
58
|
+
// Perform regex search starting at the pathname
|
|
59
|
+
const regexPattern = `<url>\\s*<loc>.*?${file.pathname.replace(
|
|
60
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
61
|
+
"\\$&"
|
|
62
|
+
)}.*?</loc>[\\s\\S]*?</url>`;
|
|
63
|
+
const match = sitemap.src.match(new RegExp(regexPattern));
|
|
70
64
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
if (match) {
|
|
66
|
+
const position = match.index; // Start position of the <url> block
|
|
67
|
+
const endPosition = match.index + match[0].length; // End position of the <url> block
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
// Replace the original <url> block with the modified one
|
|
70
|
+
sitemap.src =
|
|
71
|
+
sitemap.src.slice(0, position) +
|
|
72
|
+
entry +
|
|
73
|
+
sitemap.src.slice(endPosition);
|
|
80
74
|
} else {
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
sitemap.src = sitemap.src.replace(
|
|
76
|
+
"</urlset>",
|
|
77
|
+
`${entry}</urlset>`
|
|
78
|
+
);
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.error('Error updating sitemap:', err);
|
|
80
|
+
} else {
|
|
81
|
+
file.sitemap.pathname = sitemap.pathname;
|
|
82
|
+
sitemap.src = sitemap.src.replace(
|
|
83
|
+
"</urlset>",
|
|
84
|
+
`${entry}</urlset>`
|
|
85
|
+
);
|
|
92
86
|
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
createEntry(file) {
|
|
96
|
-
file.sitemap.loc = file.pathname;
|
|
97
|
-
file.sitemap.lastmod = file.modified.on;
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
await saveSitemap({ file: mainSitemap, host, crud });
|
|
89
|
+
await saveSitemap({ file: sitemap, host, crud });
|
|
90
|
+
await saveSitemap({ file, host, crud });
|
|
101
91
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error("Error updating sitemap:", err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
105
96
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
function createEntry(file) {
|
|
98
|
+
file.sitemap.loc = file.pathname;
|
|
99
|
+
file.sitemap.lastmod = file.modified?.on;
|
|
100
|
+
|
|
101
|
+
if (file["content-type"] === "text/html") {
|
|
102
|
+
parseHtml(file);
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
file.sitemap.type !== "news" &&
|
|
106
|
+
file.sitemap.type !== "image" &&
|
|
107
|
+
file.sitemap.type !== "video"
|
|
108
|
+
) {
|
|
109
|
+
if (!file.sitemap.changefreq)
|
|
110
|
+
file.sitemap.changefreq = "monthly";
|
|
111
|
+
|
|
112
|
+
if (!file.sitemap.priority) {
|
|
113
|
+
const depth = (file.pathname.match(/\//g) || []).length;
|
|
114
|
+
file.sitemap.priority = Math.max(
|
|
115
|
+
0.1,
|
|
116
|
+
1.0 - (depth - 1) * 0.1
|
|
117
|
+
).toFixed(1);
|
|
113
118
|
}
|
|
119
|
+
} else {
|
|
120
|
+
delete file.sitemap.changefreq;
|
|
121
|
+
delete file.sitemap.priority;
|
|
114
122
|
}
|
|
123
|
+
}
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
let entry = `\t<url>\n`;
|
|
126
|
+
|
|
127
|
+
for (const key of Object.keys(file.sitemap)) {
|
|
128
|
+
if (key === "pathname" || key === "type") continue;
|
|
129
|
+
|
|
130
|
+
let value = file.sitemap[key];
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
typeof value === "object" &&
|
|
134
|
+
value !== null &&
|
|
135
|
+
!(value instanceof Date)
|
|
136
|
+
) {
|
|
137
|
+
if (!Array.isArray(value)) value = [value];
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < value.length; i++) {
|
|
140
|
+
entry += `\t\t<${key}:${key}>\n`;
|
|
141
|
+
|
|
142
|
+
for (const nestedKey of Object.keys(value[i])) {
|
|
143
|
+
let nestedValue = value[i][nestedKey];
|
|
144
|
+
// Handle nested objects
|
|
145
|
+
if (
|
|
146
|
+
typeof nestedValue === "object" &&
|
|
147
|
+
nestedValue !== null &&
|
|
148
|
+
!(nestedValue instanceof Date)
|
|
149
|
+
) {
|
|
150
|
+
entry += `\t\t\t<${key}:${nestedKey}>\n`;
|
|
151
|
+
for (const subKey of Object.keys(nestedValue)) {
|
|
152
|
+
const subValue = nestedValue[subKey];
|
|
153
|
+
entry += `\t\t\t\t<${key}:${subKey}>${subValue}</${key}:${subKey}>\n`;
|
|
154
|
+
}
|
|
155
|
+
entry += `\t\t\t</${key}:${nestedKey}>\n`;
|
|
156
|
+
} else {
|
|
157
|
+
if (nestedKey === "loc") {
|
|
158
|
+
if (
|
|
159
|
+
!nestedValue.startsWith("https://") &&
|
|
160
|
+
!nestedValue.startsWith("http://") &&
|
|
161
|
+
!nestedValue.startsWith("{{$host}}")
|
|
162
|
+
) {
|
|
163
|
+
nestedValue = `{{$host}}${nestedValue}`;
|
|
139
164
|
}
|
|
140
|
-
|
|
165
|
+
} else if (nestedKey === "publication_date") {
|
|
166
|
+
nestedValue =
|
|
167
|
+
new Date(nestedValue)
|
|
168
|
+
.toISOString()
|
|
169
|
+
.split(".")[0] + "Z";
|
|
141
170
|
} else {
|
|
142
|
-
|
|
143
|
-
if (!nestedValue.startsWith('https://') && !nestedValue.startsWith('http://') && !nestedValue.startsWith('{{$host}}')) {
|
|
144
|
-
nestedValue = `{{$host}}${nestedValue}`;
|
|
145
|
-
}
|
|
146
|
-
} else if (nestedKey === 'publication_date') {
|
|
147
|
-
nestedValue = new Date(nestedValue).toISOString().split('.')[0] + "Z";
|
|
148
|
-
} else {
|
|
149
|
-
nestedValue = this.encodeXML(nestedValue)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
entry += `\t\t\t<${key}:${nestedKey}>${nestedValue}</${key}:${nestedKey}>\n`;
|
|
171
|
+
nestedValue = encodeXML(nestedValue);
|
|
153
172
|
}
|
|
154
|
-
}
|
|
155
173
|
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
if (key === 'loc') {
|
|
160
|
-
if (!value.startsWith('https://') && !value.startsWith('http://')) {
|
|
161
|
-
value = `{{$host}}${value}`;
|
|
174
|
+
entry += `\t\t\t<${key}:${nestedKey}>${nestedValue}</${key}:${nestedKey}>\n`;
|
|
162
175
|
}
|
|
163
|
-
} else if (key === 'lastmod') {
|
|
164
|
-
value = new Date(file.modified.on).toISOString().split('.')[0] + "Z";
|
|
165
|
-
} else {
|
|
166
|
-
value = this.encodeXML(value)
|
|
167
176
|
}
|
|
168
177
|
|
|
169
|
-
entry += `\t\t
|
|
178
|
+
entry += `\t\t</${key}:${key}>\n`;
|
|
170
179
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
pathname: '/sitemap.xml',
|
|
184
|
-
directory: '/',
|
|
185
|
-
'content-type': 'application/xml',
|
|
186
|
-
public: true,
|
|
187
|
-
organization_id: file.organization_id
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
mainSitemap = await this.readSitemap(mainSitemap, host);
|
|
191
|
-
if (!mainSitemap.src)
|
|
192
|
-
mainSitemap.src = this.createSitemap('main')
|
|
193
|
-
|
|
194
|
-
let sitemap = {
|
|
195
|
-
host: file.host,
|
|
196
|
-
path: '/',
|
|
197
|
-
pathname: file.sitemap.pathname,
|
|
198
|
-
directory: '/',
|
|
199
|
-
'content-type': 'application/xml',
|
|
200
|
-
public: true,
|
|
201
|
-
organization_id: file.organization_id
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Update loc using pathname
|
|
205
|
-
file.sitemap.loc = `${file.pathname}`
|
|
206
|
-
|
|
207
|
-
// Query the database for the correct sitemap based on the loc and type
|
|
208
|
-
if (file.sitemap.pathname) {
|
|
209
|
-
sitemap = await this.readSitemap(sitemap, host);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (!sitemap.src) {
|
|
213
|
-
let type = 'sitemap';
|
|
214
|
-
|
|
215
|
-
// Identify content type to determine sitemap type
|
|
216
|
-
if (file['content-type'].startsWith('image/')) {
|
|
217
|
-
type = 'image';
|
|
218
|
-
} else if (file['content-type'].startsWith('video/')) {
|
|
219
|
-
type = 'video';
|
|
220
|
-
} else if (file.sitemap.type === 'news') {
|
|
221
|
-
type = 'news';
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let name = `sitemap`
|
|
225
|
-
if (type === 'image' || type === 'video' || type === 'news')
|
|
226
|
-
name = `sitemap-${type}`
|
|
227
|
-
|
|
228
|
-
// If no existing sitemap found check last index sitemap
|
|
229
|
-
let index = await this.getLastSitemapIndex(mainSitemap, name);
|
|
230
|
-
if (index) {
|
|
231
|
-
sitemap.pathname = `/${name}${index}.xml`
|
|
232
|
-
sitemap = await this.readSitemap(sitemap, host);
|
|
180
|
+
} else {
|
|
181
|
+
if (key === "loc") {
|
|
182
|
+
if (
|
|
183
|
+
!value.startsWith("https://") &&
|
|
184
|
+
!value.startsWith("http://")
|
|
185
|
+
) {
|
|
186
|
+
value = `{{$host}}${value}`;
|
|
187
|
+
}
|
|
188
|
+
} else if (key === "lastmod") {
|
|
189
|
+
value =
|
|
190
|
+
new Date(file.modified?.on).toISOString().split(".")[0] +
|
|
191
|
+
"Z";
|
|
233
192
|
} else {
|
|
234
|
-
|
|
193
|
+
value = encodeXML(value);
|
|
235
194
|
}
|
|
236
195
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
index += 1
|
|
241
|
-
else
|
|
242
|
-
sitemap.src = this.createSitemap(type);
|
|
243
|
-
|
|
244
|
-
sitemap.name = `${name}${index}.xml`
|
|
245
|
-
sitemap.pathname = `/${name}${index}.xml`
|
|
196
|
+
entry += `\t\t<${key}>${value}</${key}>\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
246
199
|
|
|
247
|
-
|
|
200
|
+
entry += `\t</url>\n`;
|
|
248
201
|
|
|
249
|
-
|
|
202
|
+
return entry;
|
|
203
|
+
}
|
|
250
204
|
|
|
251
|
-
|
|
252
|
-
|
|
205
|
+
async function getSitemap({ file, host, crud }) {
|
|
206
|
+
let mainSitemap = {
|
|
207
|
+
host: file.host,
|
|
208
|
+
name: "sitemap.xml",
|
|
209
|
+
path: "/",
|
|
210
|
+
pathname: "/sitemap.xml",
|
|
211
|
+
directory: "/",
|
|
212
|
+
"content-type": "application/xml",
|
|
213
|
+
public: true,
|
|
214
|
+
organization_id: file.organization_id
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
mainSitemap = await readSitemap({ file: mainSitemap, host, crud });
|
|
218
|
+
if (!mainSitemap.src) mainSitemap.src = generateSitemapXml("main");
|
|
219
|
+
|
|
220
|
+
let sitemap = {
|
|
221
|
+
host: file.host,
|
|
222
|
+
path: "/",
|
|
223
|
+
pathname: file.sitemap.pathname,
|
|
224
|
+
directory: "/",
|
|
225
|
+
"content-type": "application/xml",
|
|
226
|
+
public: true,
|
|
227
|
+
organization_id: file.organization_id
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Update loc using pathname
|
|
231
|
+
file.sitemap.loc = `${file.pathname}`;
|
|
232
|
+
|
|
233
|
+
// Query the database for the correct sitemap based on the loc and type
|
|
234
|
+
if (file.sitemap.pathname) {
|
|
235
|
+
sitemap = await readSitemap({ file: sitemap, host, crud });
|
|
236
|
+
}
|
|
253
237
|
|
|
254
|
-
|
|
255
|
-
|
|
238
|
+
if (!sitemap.src) {
|
|
239
|
+
let type = "sitemap";
|
|
256
240
|
|
|
257
|
-
//
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
241
|
+
// Identify content type to determine sitemap type
|
|
242
|
+
if (file["content-type"].startsWith("image/")) {
|
|
243
|
+
type = "image";
|
|
244
|
+
} else if (file["content-type"].startsWith("video/")) {
|
|
245
|
+
type = "video";
|
|
246
|
+
} else if (file.sitemap.type === "news") {
|
|
247
|
+
type = "news";
|
|
262
248
|
}
|
|
263
249
|
|
|
264
|
-
|
|
265
|
-
|
|
250
|
+
let name = `sitemap`;
|
|
251
|
+
if (type === "image" || type === "video" || type === "news")
|
|
252
|
+
name = `sitemap-${type}`;
|
|
266
253
|
|
|
267
|
-
|
|
268
|
-
let
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
host: { $in: [host, '*'] },
|
|
275
|
-
pathname: file.pathname
|
|
276
|
-
},
|
|
277
|
-
limit: 1
|
|
278
|
-
},
|
|
279
|
-
organization_id: file.organization_id
|
|
254
|
+
// If no existing sitemap found check last index sitemap
|
|
255
|
+
let index = await getLastSitemapIndex(mainSitemap, name);
|
|
256
|
+
if (index) {
|
|
257
|
+
sitemap.pathname = `/${name}${index}.xml`;
|
|
258
|
+
sitemap = await readSitemap({ file: sitemap, host, crud });
|
|
259
|
+
} else {
|
|
260
|
+
index = 1;
|
|
280
261
|
}
|
|
281
|
-
data = await this.crud.send(data)
|
|
282
|
-
if (data.object && data.object.length)
|
|
283
|
-
return data.object[0]
|
|
284
|
-
else
|
|
285
|
-
return file
|
|
286
|
-
}
|
|
287
262
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
263
|
+
// Check if there's room in the last index sitemap
|
|
264
|
+
if (!checkSitemapXml(sitemap.src)) {
|
|
265
|
+
if (sitemap.src) index += 1;
|
|
266
|
+
else sitemap.src = generateSitemapXml(type);
|
|
291
267
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
} else {
|
|
295
|
-
const imageNamespace = 'xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"';
|
|
296
|
-
const videoNamespace = 'xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
|
|
297
|
-
const newsNamespace = 'xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"';
|
|
298
|
-
|
|
299
|
-
if (type === 'image') {
|
|
300
|
-
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${imageNamespace}>\n</urlset>`;
|
|
301
|
-
} else if (type === 'video') {
|
|
302
|
-
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${videoNamespace}>\n</urlset>`;
|
|
303
|
-
} else { // For 'news' type or any other types
|
|
304
|
-
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${newsNamespace} ${imageNamespace} ${videoNamespace}>\n</urlset>`;
|
|
305
|
-
}
|
|
268
|
+
sitemap.name = `${name}${index}.xml`;
|
|
269
|
+
sitemap.pathname = `/${name}${index}.xml`;
|
|
306
270
|
}
|
|
307
271
|
}
|
|
308
272
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
method: 'object.update',
|
|
312
|
-
host: host,
|
|
313
|
-
array: 'files',
|
|
314
|
-
object: file,
|
|
315
|
-
upsert: true,
|
|
316
|
-
organization_id: file.organization_id
|
|
317
|
-
}
|
|
318
|
-
if (!file._id)
|
|
319
|
-
data.$filter = {
|
|
320
|
-
query: {
|
|
321
|
-
host: { $in: [host, '*'] },
|
|
322
|
-
pathname: file.pathname
|
|
323
|
-
},
|
|
324
|
-
limit: 1
|
|
325
|
-
}
|
|
273
|
+
// Create the regex pattern to match the <sitemap> block containing the specific <loc> for the pathname
|
|
274
|
+
const regexPattern = `<sitemap>\\s*<loc>[^<]*${sitemap.pathname}[^<]*</loc>[\\s\\S]*?</sitemap>`;
|
|
326
275
|
|
|
327
|
-
|
|
276
|
+
// Execute the regex match against the sitemap index source
|
|
277
|
+
const match = mainSitemap.src.match(new RegExp(regexPattern));
|
|
328
278
|
|
|
279
|
+
// Check if a match is found
|
|
280
|
+
if (!match) {
|
|
281
|
+
const indexEntry = `\t<sitemap>\n\t\t<loc>{{$host}}${sitemap.pathname}</loc>\n</sitemap>`;
|
|
282
|
+
mainSitemap.src = mainSitemap.src.replace(
|
|
283
|
+
"</sitemapindex>",
|
|
284
|
+
`${indexEntry}\n</sitemapindex>`
|
|
285
|
+
);
|
|
329
286
|
}
|
|
330
287
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
// Use regex to match all sitemap entries for the given type
|
|
334
|
-
const regex = new RegExp(`\\/${filename}(\\d*)\\.xml<\\/loc>`, 'g');
|
|
335
|
-
const matches = mainSitemap.src.match(regex);
|
|
288
|
+
return { mainSitemap, sitemap };
|
|
289
|
+
}
|
|
336
290
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
291
|
+
async function readSitemap({ file, host, crud }) {
|
|
292
|
+
let data = {
|
|
293
|
+
method: "object.read",
|
|
294
|
+
host: host,
|
|
295
|
+
array: "files",
|
|
296
|
+
$filter: {
|
|
297
|
+
query: {
|
|
298
|
+
host: { $in: [host, "*"] },
|
|
299
|
+
pathname: file.pathname
|
|
300
|
+
},
|
|
301
|
+
limit: 1
|
|
302
|
+
},
|
|
303
|
+
organization_id: file.organization_id
|
|
304
|
+
};
|
|
305
|
+
data = await crud.send(data);
|
|
306
|
+
if (data.object && data.object.length) return data.object[0];
|
|
307
|
+
else return file;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function generateSitemapXml(type) {
|
|
311
|
+
const xmlDeclaration = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
|
312
|
+
const sitemapNamespace =
|
|
313
|
+
'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
|
|
314
|
+
|
|
315
|
+
if (type === "main") {
|
|
316
|
+
return `${xmlDeclaration}<sitemapindex ${sitemapNamespace}>\n</sitemapindex>`;
|
|
317
|
+
} else {
|
|
318
|
+
const imageNamespace =
|
|
319
|
+
'xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"';
|
|
320
|
+
const videoNamespace =
|
|
321
|
+
'xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
|
|
322
|
+
const newsNamespace =
|
|
323
|
+
'xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"';
|
|
324
|
+
|
|
325
|
+
if (type === "image") {
|
|
326
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${imageNamespace}>\n</urlset>`;
|
|
327
|
+
} else if (type === "video") {
|
|
328
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${videoNamespace}>\n</urlset>`;
|
|
329
|
+
} else {
|
|
330
|
+
// For 'news' type or any other types
|
|
331
|
+
return `${xmlDeclaration}<urlset ${sitemapNamespace} ${newsNamespace} ${imageNamespace} ${videoNamespace}>\n</urlset>`;
|
|
341
332
|
}
|
|
342
333
|
}
|
|
334
|
+
}
|
|
343
335
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
336
|
+
async function saveSitemap({ file, host, crud }) {
|
|
337
|
+
let data = {
|
|
338
|
+
method: "object.update",
|
|
339
|
+
host: host,
|
|
340
|
+
array: "files",
|
|
341
|
+
object: file,
|
|
342
|
+
upsert: true,
|
|
343
|
+
organization_id: file.organization_id
|
|
344
|
+
};
|
|
345
|
+
if (!file._id)
|
|
346
|
+
data.$filter = {
|
|
347
|
+
query: {
|
|
348
|
+
host: { $in: [host, "*"] },
|
|
349
|
+
pathname: file.pathname
|
|
350
|
+
},
|
|
351
|
+
limit: 1
|
|
352
|
+
};
|
|
359
353
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return false;
|
|
354
|
+
await crud.send(data);
|
|
355
|
+
}
|
|
363
356
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
357
|
+
async function getLastSitemapIndex(mainSitemap, filename) {
|
|
358
|
+
try {
|
|
359
|
+
// Use regex to match all sitemap entries for the given type
|
|
360
|
+
const regex = new RegExp(`\\/${filename}(\\d*)\\.xml<\\/loc>`, "g");
|
|
361
|
+
const matches = mainSitemap.src.match(regex);
|
|
362
|
+
|
|
363
|
+
return matches ? matches.length : null;
|
|
364
|
+
} catch (err) {
|
|
365
|
+
console.error(
|
|
366
|
+
`Error determining next sitemap index for ${filename}:`,
|
|
367
|
+
err
|
|
368
|
+
);
|
|
369
|
+
return null;
|
|
369
370
|
}
|
|
371
|
+
}
|
|
370
372
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
373
|
+
function checkSitemapXml(sitemap) {
|
|
374
|
+
try {
|
|
375
|
+
if (!sitemap) return false;
|
|
374
376
|
|
|
375
|
-
|
|
377
|
+
// Count the number of <url> entries
|
|
378
|
+
const urlCount = (sitemap.match(/<url>/g) || []).length;
|
|
379
|
+
if (urlCount >= 50000) return false;
|
|
376
380
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
continue
|
|
381
|
-
if (Array.isArray(file.sitemap[types[i]]))
|
|
382
|
-
previousEntries[types[i]] = file.sitemap[types[i]]
|
|
383
|
-
else
|
|
384
|
-
previousEntries[types[i]] = [file.sitemap[types[i]]]
|
|
381
|
+
// Get the size of the sitemap string in bytes
|
|
382
|
+
const fileSizeInBytes = Buffer.byteLength(sitemap, "utf8");
|
|
383
|
+
const fileSizeInMB = fileSizeInBytes / (1024 * 1024);
|
|
385
384
|
|
|
386
|
-
|
|
387
|
-
|
|
385
|
+
// Check if the file size exceeds either the 50MB limit or the MongoDB 16MB limit
|
|
386
|
+
if (fileSizeInMB >= 50 || fileSizeInMB >= 15) return false;
|
|
388
387
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
type = 'image';
|
|
396
|
-
query = 'loc'
|
|
397
|
-
entryObject.loc = entries[i].getAttribute('src');
|
|
398
|
-
entryObject.title = entries[i].getAttribute('sitemap-title') || entries[i].getAttribute('title') || entries[i].getAttribute('alt');
|
|
399
|
-
entryObject.caption = entries[i].getAttribute('sitemap-caption') || entries[i].getAttribute('alt') || entryObject.title;
|
|
400
|
-
entryObject.geo_location = entries[i].getAttribute('sitemap-geo-location');
|
|
401
|
-
} else if (entries[i].tagName === 'VIDEO') {
|
|
402
|
-
type = 'video';
|
|
403
|
-
query = 'content_loc'
|
|
404
|
-
entryObject.content_loc = entries[i].src;
|
|
405
|
-
entryObject.title = entries[i].getAttribute('sitemap-title') || entries[i].getAttribute('title');
|
|
406
|
-
entryObject.description = entries[i].getAttribute('description'); // 'description' if available
|
|
407
|
-
entryObject.thumbnail_loc = entries[i].getAttribute('sitemap-thumbnail') || entries[i].getAttribute('poster');
|
|
408
|
-
entryObject.duration = entries[i].getAttribute('sitemap-duration');
|
|
409
|
-
} else {
|
|
410
|
-
type = 'news';
|
|
411
|
-
file.sitemap.type = 'news';
|
|
412
|
-
query = 'title'
|
|
413
|
-
entryObject.title = entries[i].getAttribute('sitemap-title');
|
|
414
|
-
if (!entryObject.title) {
|
|
415
|
-
const title = dom.querySelector('title');
|
|
416
|
-
entryObject.title = title ? title.text : '';
|
|
417
|
-
}
|
|
388
|
+
return true;
|
|
389
|
+
} catch (err) {
|
|
390
|
+
console.error("Error checking sitemap file:", err);
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
418
394
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
};
|
|
395
|
+
function parseHtml(file) {
|
|
396
|
+
const dom = parse(file.src);
|
|
397
|
+
const entries = dom.querySelectorAll('[sitemap="true"]');
|
|
423
398
|
|
|
424
|
-
|
|
425
|
-
// Fallback to HTML lang attribute
|
|
426
|
-
const htmlElement = dom.querySelector('html');
|
|
427
|
-
entryObject.publication.language = htmlElement ? htmlElement.getAttribute('lang') : null;
|
|
428
|
-
}
|
|
399
|
+
let types = ["image", "video", "news"];
|
|
429
400
|
|
|
430
|
-
|
|
401
|
+
const previousEntries = {};
|
|
402
|
+
for (let i = 0; i < types.length; i++) {
|
|
403
|
+
if (!file.sitemap[types[i]]) continue;
|
|
404
|
+
if (Array.isArray(file.sitemap[types[i]]))
|
|
405
|
+
previousEntries[types[i]] = file.sitemap[types[i]];
|
|
406
|
+
else previousEntries[types[i]] = [file.sitemap[types[i]]];
|
|
431
407
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const keywords = dom.querySelector('meta[name="keywords"]');
|
|
435
|
-
entryObject.keywords = keywords ? keywords.getAttribute('content') : '';
|
|
436
|
-
}
|
|
408
|
+
delete file.sitemap[types[i]];
|
|
409
|
+
}
|
|
437
410
|
|
|
438
|
-
|
|
411
|
+
for (let i = 0; i < entries.length; i++) {
|
|
412
|
+
let type = "",
|
|
413
|
+
query = "";
|
|
414
|
+
let existingObject;
|
|
415
|
+
let entryObject = {};
|
|
416
|
+
|
|
417
|
+
if (entries[i].tagName === "IMG") {
|
|
418
|
+
type = "image";
|
|
419
|
+
query = "loc";
|
|
420
|
+
entryObject.loc = entries[i].getAttribute("src");
|
|
421
|
+
entryObject.title =
|
|
422
|
+
entries[i].getAttribute("sitemap-title") ||
|
|
423
|
+
entries[i].getAttribute("title") ||
|
|
424
|
+
entries[i].getAttribute("alt");
|
|
425
|
+
entryObject.caption =
|
|
426
|
+
entries[i].getAttribute("sitemap-caption") ||
|
|
427
|
+
entries[i].getAttribute("alt") ||
|
|
428
|
+
entryObject.title;
|
|
429
|
+
entryObject.geo_location = entries[i].getAttribute(
|
|
430
|
+
"sitemap-geo-location"
|
|
431
|
+
);
|
|
432
|
+
} else if (entries[i].tagName === "VIDEO") {
|
|
433
|
+
type = "video";
|
|
434
|
+
query = "content_loc";
|
|
435
|
+
entryObject.content_loc = entries[i].src;
|
|
436
|
+
entryObject.title =
|
|
437
|
+
entries[i].getAttribute("sitemap-title") ||
|
|
438
|
+
entries[i].getAttribute("title");
|
|
439
|
+
entryObject.description =
|
|
440
|
+
entries[i].getAttribute("description");
|
|
441
|
+
entryObject.thumbnail_loc =
|
|
442
|
+
entries[i].getAttribute("sitemap-thumbnail") ||
|
|
443
|
+
entries[i].getAttribute("poster");
|
|
444
|
+
entryObject.duration =
|
|
445
|
+
entries[i].getAttribute("sitemap-duration");
|
|
446
|
+
} else {
|
|
447
|
+
type = "news";
|
|
448
|
+
file.sitemap.type = "news";
|
|
449
|
+
query = "title";
|
|
450
|
+
entryObject.title = entries[i].getAttribute("sitemap-title");
|
|
451
|
+
if (!entryObject.title) {
|
|
452
|
+
const title = dom.querySelector("title");
|
|
453
|
+
entryObject.title = title ? title.text : "";
|
|
439
454
|
}
|
|
440
455
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
456
|
+
entryObject.publication = {
|
|
457
|
+
name: entries[i].getAttribute("sitemap-publication-name"),
|
|
458
|
+
language: entries[i].getAttribute(
|
|
459
|
+
"sitemap-publication-language"
|
|
460
|
+
)
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
if (!entryObject.publication.language) {
|
|
464
|
+
const htmlElement = dom.querySelector("html");
|
|
465
|
+
entryObject.publication.language = htmlElement
|
|
466
|
+
? htmlElement.getAttribute("lang")
|
|
467
|
+
: null;
|
|
444
468
|
}
|
|
445
469
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
470
|
+
entryObject.publication_date =
|
|
471
|
+
entries[i].getAttribute("sitemap-publication-date") ||
|
|
472
|
+
file.modified?.on;
|
|
473
|
+
|
|
474
|
+
entryObject.keywords =
|
|
475
|
+
entries[i].getAttribute("sitemap-keywords");
|
|
476
|
+
if (!entryObject.keywords) {
|
|
477
|
+
const keywords = dom.querySelector('meta[name="keywords"]');
|
|
478
|
+
entryObject.keywords = keywords
|
|
479
|
+
? keywords.getAttribute("content")
|
|
480
|
+
: "";
|
|
481
|
+
}
|
|
455
482
|
|
|
483
|
+
entryObject.genres = entries[i].getAttribute("sitemap-genres");
|
|
456
484
|
}
|
|
457
485
|
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
486
|
+
if (previousEntries[type]) {
|
|
487
|
+
existingObject = previousEntries[type].find(
|
|
488
|
+
(item) => item[query] === entryObject[query]
|
|
489
|
+
);
|
|
490
|
+
entryObject = { ...existingObject, ...entryObject };
|
|
463
491
|
}
|
|
464
492
|
|
|
465
|
-
|
|
493
|
+
Object.keys(entryObject).forEach((key) => {
|
|
494
|
+
if (!entryObject[key]) delete entryObject[key];
|
|
495
|
+
});
|
|
466
496
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
.replace(/&/g, '&')
|
|
471
|
-
.replace(/</g, '<')
|
|
472
|
-
.replace(/>/g, '>')
|
|
473
|
-
.replace(/"/g, '"')
|
|
474
|
-
.replace(/'/g, ''');
|
|
497
|
+
if (!file.sitemap[type]) file.sitemap[type] = [];
|
|
498
|
+
|
|
499
|
+
file.sitemap[type].push(entryObject);
|
|
475
500
|
}
|
|
476
501
|
|
|
502
|
+
if (
|
|
503
|
+
file.sitemap.type !== "news" &&
|
|
504
|
+
file.sitemap.type !== "image" &&
|
|
505
|
+
file.sitemap.type !== "video"
|
|
506
|
+
) {
|
|
507
|
+
const priorityMeta = dom.querySelector(
|
|
508
|
+
'meta[name="sitemap-priority"]'
|
|
509
|
+
);
|
|
510
|
+
const changefreqMeta = dom.querySelector(
|
|
511
|
+
'meta[name="sitemap-changefreq"]'
|
|
512
|
+
);
|
|
513
|
+
file.sitemap.priority = priorityMeta
|
|
514
|
+
? priorityMeta.getAttribute("content")
|
|
515
|
+
: file.sitemap.priority;
|
|
516
|
+
file.sitemap.changefreq = changefreqMeta
|
|
517
|
+
? changefreqMeta.getAttribute("content")
|
|
518
|
+
: file.sitemap.changefreq || "monthly";
|
|
519
|
+
}
|
|
477
520
|
}
|
|
478
521
|
|
|
479
|
-
|
|
522
|
+
function encodeXML(str) {
|
|
523
|
+
if (str)
|
|
524
|
+
return str
|
|
525
|
+
.replace(/&/g, "&")
|
|
526
|
+
.replace(/</g, "<")
|
|
527
|
+
.replace(/>/g, ">")
|
|
528
|
+
.replace(/"/g, """)
|
|
529
|
+
.replace(/'/g, "'");
|
|
530
|
+
}
|