diff --git a/package-lock.json b/package-lock.json index 837177c7acf7..afff3d9c026d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "clsx": "^2.1.0", "cssnano": "^6.0.3", "dotenv": "^16.4.4", + "fast-xml-parser": "^4.5.0", "fs-extra": "^11.2.0", "fuse.js": "^7.0.0", "googleapis": "^133.0.0", @@ -13859,6 +13860,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -27471,6 +27494,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/style-loader": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", diff --git a/package.json b/package.json index 3885874ae365..6af7ec597c27 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "clsx": "^2.1.0", "cssnano": "^6.0.3", "dotenv": "^16.4.4", + "fast-xml-parser": "^4.5.0", "fs-extra": "^11.2.0", "fuse.js": "^7.0.0", "googleapis": "^133.0.0", diff --git a/scripts/build-rss.js b/scripts/build-rss.js index 933ec63d69d1..673da1398fe0 100644 --- a/scripts/build-rss.js +++ b/scripts/build-rss.js @@ -60,7 +60,7 @@ module.exports = async function rssFeed(type, title, desc, outputPath) { ); if (invalidPosts.length > 0) { - throw new Error('Missing required fields in post data'); + throw new Error(`Missing required fields in posts: ${invalidPosts.map(p => p.title || p.slug).join(', ')}`); } for (let post of posts) { diff --git a/tests/build-rss.test.js b/tests/build-rss.test.js index 367e196ad022..8468bf3365a4 100644 --- a/tests/build-rss.test.js +++ b/tests/build-rss.test.js @@ -1,6 +1,8 @@ const fs = require('fs'); const path = require('path'); const rssFeed = require('../scripts/build-rss'); +const { XMLParser } = require('fast-xml-parser'); +const parser = new XMLParser({ ignoreAttributes: false }); const { mockRssData, title, type, desc, missingDateMockData, incompletePostMockData } = require('./fixtures/rssData'); describe('rssFeed', () => { @@ -49,11 +51,12 @@ describe('rssFeed', () => { const filePath = path.join(__dirname, '..', 'public', outputPath); const fileContent = fs.readFileSync(filePath, 'utf8'); - const itemTitles = fileContent.match(/(.*?)<\/title>/g); + const parsedContent = parser.parse(fileContent); + const itemTitles = parsedContent.rss.channel.item.map(item => item.title); - expect(itemTitles[1]).toContain('Test Post 1'); - expect(itemTitles[2]).toContain('Another Featured Post'); - expect(itemTitles[3]).toContain('Non-Featured Post 1'); + expect(itemTitles[0]).toContain('Test Post 1'); + expect(itemTitles[1]).toContain('Another Featured Post'); + expect(itemTitles[2]).toContain('Non-Featured Post 1'); }); it('should sort posts by date in descending order', async () => { @@ -64,13 +67,14 @@ describe('rssFeed', () => { const filePath = path.join(__dirname, '..', 'public', outputPath); const fileContent = fs.readFileSync(filePath, 'utf8'); - const itemTitles = fileContent.match(/<title>(.*?)<\/title>/g); + const parsedContent = parser.parse(fileContent); + const itemTitles = parsedContent.rss.channel.item.map(item => item.title); - expect(itemTitles[1]).toContain('Test Post 1'); - expect(itemTitles[2]).toContain('Another Featured Post'); - expect(itemTitles[3]).toContain('Non-Featured Post 1'); - expect(itemTitles[4]).toContain('Non-Featured Post 3'); - expect(itemTitles[5]).toContain('Non-Featured Post 2'); + expect(itemTitles[0]).toContain('Test Post 1'); + expect(itemTitles[1]).toContain('Another Featured Post'); + expect(itemTitles[2]).toContain('Non-Featured Post 1'); + expect(itemTitles[3]).toContain('Non-Featured Post 3'); + expect(itemTitles[4]).toContain('Non-Featured Post 2'); }); it('should set correct enclosure type based on image extension', async () => {