@tmlmobilidade/rss 20260406.1418.25 → 20260407.1655.55
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/dist/components/RSSFeedItem/index.d.ts +2 -0
- package/dist/components/RSSFeedItem/index.js +30 -0
- package/dist/{utils/rss-feed-xml.d.ts → components/RssXML/index.d.ts} +2 -1
- package/dist/{utils/rss-feed-xml.js → components/RssXML/index.js} +15 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -3
- package/dist/types/feed.types.d.ts +10 -2
- package/dist/types/feed.types.js +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/normalized-rss-image.d.ts +6 -0
- package/dist/types/normalized-rss-image.js +1 -0
- package/dist/utils/build-content-encoded.d.ts +2 -0
- package/dist/utils/build-content-encoded.js +37 -0
- package/dist/utils/normalize-image-inputs.d.ts +3 -0
- package/dist/utils/normalize-image-inputs.js +27 -0
- package/package.json +1 -1
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/index.js +0 -3
- package/dist/utils/rss-item-xml.d.ts +0 -2
- package/dist/utils/rss-item-xml.js +0 -15
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { buildContentEncoded } from '../../utils/build-content-encoded.js';
|
|
3
|
+
import { escapeXml } from '../../utils/escape-xml.js';
|
|
4
|
+
import { normalizeImageInputs } from '../../utils/normalize-image-inputs.js';
|
|
5
|
+
/* * */
|
|
6
|
+
export function rssFeedItem(item) {
|
|
7
|
+
//
|
|
8
|
+
//
|
|
9
|
+
// A. Transform Data
|
|
10
|
+
const enclosuresXml = normalizeImageInputs(item.images ?? []).map((image) => {
|
|
11
|
+
const lengthAttr = image.length != null ? ` length="${String(image.length)}"` : '';
|
|
12
|
+
const typeAttr = image.type ? ` type="${escapeXml(image.type)}"` : '';
|
|
13
|
+
return `<enclosure url="${escapeXml(image.url)}"${lengthAttr}${typeAttr} />`;
|
|
14
|
+
});
|
|
15
|
+
//
|
|
16
|
+
// B. Return Result
|
|
17
|
+
return [
|
|
18
|
+
'<item>',
|
|
19
|
+
`<title>${escapeXml(item.title || '')}</title>`,
|
|
20
|
+
`<link>${escapeXml(item.link || '')}</link>`,
|
|
21
|
+
`<guid isPermaLink="true">${escapeXml(item.link || '')}</guid>`,
|
|
22
|
+
item.publishDate ? `<pubDate>${escapeXml(item.publishDate)}</pubDate>` : '',
|
|
23
|
+
`<description>${escapeXml(item.description)}</description>`,
|
|
24
|
+
buildContentEncoded(item),
|
|
25
|
+
enclosuresXml.length ? enclosuresXml.join('\n') : '',
|
|
26
|
+
'</item>',
|
|
27
|
+
].filter(Boolean).join('\n');
|
|
28
|
+
//
|
|
29
|
+
}
|
|
30
|
+
/* * */
|
|
@@ -2,6 +2,7 @@ interface RssChannelOptions {
|
|
|
2
2
|
channelCopyright: string;
|
|
3
3
|
channelDescription: string;
|
|
4
4
|
channelDocs: string;
|
|
5
|
+
channelFeedSelfUrl?: string;
|
|
5
6
|
channelGenerator: string;
|
|
6
7
|
channelLanguage: string;
|
|
7
8
|
channelLastBuildDate: string;
|
|
@@ -9,5 +10,5 @@ interface RssChannelOptions {
|
|
|
9
10
|
channelPubDate: string;
|
|
10
11
|
channelTitle: string;
|
|
11
12
|
}
|
|
12
|
-
export declare function
|
|
13
|
+
export declare function rssFeed(itemsXml: string, channelOptions: RssChannelOptions): string;
|
|
13
14
|
export {};
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/* * */
|
|
2
|
-
import { escapeXml } from '
|
|
3
|
-
|
|
2
|
+
import { escapeXml } from '../../utils/escape-xml.js';
|
|
3
|
+
/* * */
|
|
4
|
+
export function rssFeed(itemsXml, channelOptions) {
|
|
5
|
+
//
|
|
6
|
+
//
|
|
7
|
+
// A. Setup Variables
|
|
8
|
+
const rssOpenTag = '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">';
|
|
9
|
+
//
|
|
10
|
+
// B. Return Result
|
|
4
11
|
return [
|
|
5
12
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
6
|
-
|
|
13
|
+
rssOpenTag,
|
|
7
14
|
'<channel>',
|
|
8
|
-
`<title>${escapeXml(channelOptions.channelTitle)}</title>`,
|
|
9
15
|
`<link>${escapeXml(channelOptions.channelLink)}</link>`,
|
|
10
16
|
`<description>${escapeXml(channelOptions.channelDescription)}</description>`,
|
|
11
17
|
`<language>${escapeXml(channelOptions.channelLanguage)}</language>`,
|
|
@@ -14,9 +20,13 @@ export function rssFeedXml(itemsXml, channelOptions) {
|
|
|
14
20
|
`<docs>${escapeXml(channelOptions.channelDocs)}</docs>`,
|
|
15
21
|
`<pubDate>${escapeXml(channelOptions.channelPubDate)}</pubDate>`,
|
|
16
22
|
`<copyright>${escapeXml(channelOptions.channelCopyright)}</copyright>`,
|
|
23
|
+
channelOptions.channelFeedSelfUrl
|
|
24
|
+
? `<atom:link href="${escapeXml(channelOptions.channelFeedSelfUrl)}" rel="self" type="application/rss+xml" />`
|
|
25
|
+
: '',
|
|
17
26
|
itemsXml,
|
|
18
27
|
'</channel>',
|
|
19
28
|
'</rss>',
|
|
20
|
-
].join('\n');
|
|
29
|
+
].filter(Boolean).join('\n');
|
|
30
|
+
//
|
|
21
31
|
}
|
|
22
32
|
/* * */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CreateRssFeedOptions, RssRawItem } from './types/index.js';
|
|
2
|
+
export type { RssRawImageInput } from './types/index.js';
|
|
2
3
|
export declare function createRssFeed(rawItems: RssRawItem[], options: CreateRssFeedOptions): string;
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
/* * */
|
|
2
|
-
import {
|
|
2
|
+
import { rssFeedItem } from './components/RSSFeedItem/index.js';
|
|
3
|
+
import { rssFeed } from './components/RssXML/index.js';
|
|
3
4
|
/* * */
|
|
4
5
|
export function createRssFeed(rawItems, options) {
|
|
5
6
|
//
|
|
6
7
|
//
|
|
7
8
|
// A. Create XML items
|
|
8
|
-
const itemsXml = rawItems.map(item =>
|
|
9
|
+
const itemsXml = rawItems.map(item => rssFeedItem({
|
|
10
|
+
contentHtml: item.contentHtml,
|
|
9
11
|
description: item.summary || item.description || '',
|
|
12
|
+
images: item.images,
|
|
10
13
|
link: item.link || '',
|
|
14
|
+
linkLabel: item.linkLabel,
|
|
11
15
|
publishDate: item.publishDate || item.publish_start_date || item.created_at ? new Date(item.publishDate || item.publish_start_date || item.created_at).toUTCString() : undefined,
|
|
12
16
|
title: item.title || '',
|
|
13
17
|
})).join('\n');
|
|
14
18
|
//
|
|
15
19
|
// B. Create and return XML feed
|
|
16
20
|
const now = new Date().toUTCString();
|
|
17
|
-
return
|
|
21
|
+
return rssFeed(itemsXml, {
|
|
18
22
|
channelCopyright: options.copyright,
|
|
19
23
|
channelDescription: options.description,
|
|
20
24
|
channelDocs: 'https://www.rssboard.org/rss-specification',
|
|
25
|
+
channelFeedSelfUrl: options.feedSelfUrl,
|
|
21
26
|
channelGenerator: '@tmlmobilidade/rss',
|
|
22
27
|
channelLanguage: 'pt-pt',
|
|
23
28
|
channelLastBuildDate: now,
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
export interface RssRawImageInput {
|
|
2
|
+
alt?: null | string;
|
|
3
|
+
length?: null | number;
|
|
4
|
+
type?: null | string;
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
1
7
|
export interface RssRawItem {
|
|
2
8
|
_id?: string;
|
|
9
|
+
contentHtml?: null | string;
|
|
3
10
|
created_at?: number | string;
|
|
4
11
|
description?: null | string;
|
|
5
|
-
|
|
6
|
-
info_url?: null | string;
|
|
12
|
+
images?: Array<RssRawImageInput>;
|
|
7
13
|
link?: null | string;
|
|
14
|
+
linkLabel?: null | string;
|
|
8
15
|
publish_start_date?: null | number;
|
|
9
16
|
publishDate?: null | string;
|
|
10
17
|
slug?: null | string;
|
|
@@ -14,6 +21,7 @@ export interface RssRawItem {
|
|
|
14
21
|
export interface CreateRssFeedOptions {
|
|
15
22
|
copyright: string;
|
|
16
23
|
description: string;
|
|
24
|
+
feedSelfUrl?: string;
|
|
17
25
|
link: string;
|
|
18
26
|
title: string;
|
|
19
27
|
}
|
package/dist/types/feed.types.js
CHANGED
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { escapeXml } from './escape-xml.js';
|
|
3
|
+
import { normalizeImageInputs } from './normalize-image-inputs.js';
|
|
4
|
+
/* * */
|
|
5
|
+
export function buildContentEncoded(item) {
|
|
6
|
+
//
|
|
7
|
+
//
|
|
8
|
+
// A. Setup Variables
|
|
9
|
+
const parts = [];
|
|
10
|
+
const bodyHtml = escapeXml(item.description).replace(/\n/g, '<br />');
|
|
11
|
+
//
|
|
12
|
+
// B. Transform Data
|
|
13
|
+
const cdataSafe = (fragment) => {
|
|
14
|
+
return fragment.replace(/\]\]>/g, ']]]]><![CDATA[>');
|
|
15
|
+
};
|
|
16
|
+
if (item.contentHtml) {
|
|
17
|
+
return `<content:encoded><![CDATA[${cdataSafe(item.contentHtml)}]]></content:encoded>`;
|
|
18
|
+
}
|
|
19
|
+
if (item.description) {
|
|
20
|
+
parts.push(`<div>${bodyHtml}</div>`);
|
|
21
|
+
}
|
|
22
|
+
for (const image of normalizeImageInputs(item.images ?? [])) {
|
|
23
|
+
const alt = image.alt ? escapeXml(image.alt) : '';
|
|
24
|
+
parts.push(`<p>`
|
|
25
|
+
+ `<img src="${escapeXml(image.url)}" alt="${alt}" />`
|
|
26
|
+
+ `</p>`);
|
|
27
|
+
}
|
|
28
|
+
if (item.link) {
|
|
29
|
+
parts.push(`<p>`
|
|
30
|
+
+ `<a href="${escapeXml(item.link)}">${escapeXml(item.linkLabel ?? 'Ler artigo completo')}</a>`
|
|
31
|
+
+ `</p>`);
|
|
32
|
+
}
|
|
33
|
+
//
|
|
34
|
+
// C. Return Result
|
|
35
|
+
return `<content:encoded><![CDATA[${cdataSafe(parts.join('\n'))}]]></content:encoded>`;
|
|
36
|
+
//
|
|
37
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
/* * */
|
|
3
|
+
export function normalizeImageInputs(images) {
|
|
4
|
+
//
|
|
5
|
+
//
|
|
6
|
+
// A. Setup Variables
|
|
7
|
+
const normalizedImages = [];
|
|
8
|
+
//
|
|
9
|
+
// B. Transform Data
|
|
10
|
+
for (const image of images ?? []) {
|
|
11
|
+
const url = image.url?.trim() ?? '';
|
|
12
|
+
const normalizedImage = { url };
|
|
13
|
+
if (!url)
|
|
14
|
+
continue;
|
|
15
|
+
if (image.alt)
|
|
16
|
+
normalizedImage.alt = image.alt;
|
|
17
|
+
if (image.type)
|
|
18
|
+
normalizedImage.type = image.type;
|
|
19
|
+
if (image.length != null && Number.isFinite(image.length))
|
|
20
|
+
normalizedImage.length = Math.floor(image.length);
|
|
21
|
+
normalizedImages.push(normalizedImage);
|
|
22
|
+
}
|
|
23
|
+
//
|
|
24
|
+
// C. Return Result
|
|
25
|
+
return normalizedImages;
|
|
26
|
+
//
|
|
27
|
+
}
|
package/package.json
CHANGED
package/dist/utils/index.d.ts
DELETED
package/dist/utils/index.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/* * */
|
|
2
|
-
import { escapeXml } from './escape-xml.js';
|
|
3
|
-
/* * */
|
|
4
|
-
export function rssItemXml(item) {
|
|
5
|
-
return [
|
|
6
|
-
'<item>',
|
|
7
|
-
`<title>${escapeXml(item.title)}</title>`,
|
|
8
|
-
`<link>${escapeXml(item.link || '')}</link>`,
|
|
9
|
-
`<guid isPermaLink="true">${escapeXml(item.link || '')}</guid>`,
|
|
10
|
-
item.publishDate ? `<pubDate>${escapeXml(item.publishDate)}</pubDate>` : '',
|
|
11
|
-
`<description>${escapeXml(item.description)}</description>`,
|
|
12
|
-
'</item>',
|
|
13
|
-
].filter(Boolean).join('\n');
|
|
14
|
-
}
|
|
15
|
-
/* * */
|