160 lines
4.7 KiB
TypeScript
160 lines
4.7 KiB
TypeScript
import * as plugins from './smartsitemap.plugins.js';
|
|
import type * as interfaces from './interfaces/index.js';
|
|
|
|
/**
|
|
* Imports RSS/Atom feeds and converts them to sitemap URL entries.
|
|
* This is a unique feature of smartsitemap that competitors don't offer.
|
|
*/
|
|
export class FeedImporter {
|
|
/**
|
|
* Import from a feed URL, returning standard sitemap URL entries.
|
|
*/
|
|
static async fromUrl(
|
|
feedUrl: string,
|
|
options?: interfaces.IFeedImportOptions,
|
|
): Promise<interfaces.ISitemapUrl[]> {
|
|
const smartfeed = new plugins.smartfeed.Smartfeed();
|
|
const feed = await smartfeed.parseFeedFromUrl(feedUrl);
|
|
return FeedImporter.mapItems(feed.items, options);
|
|
}
|
|
|
|
/**
|
|
* Import from a feed XML string, returning standard sitemap URL entries.
|
|
*/
|
|
static async fromString(
|
|
feedXml: string,
|
|
options?: interfaces.IFeedImportOptions,
|
|
): Promise<interfaces.ISitemapUrl[]> {
|
|
const smartfeed = new plugins.smartfeed.Smartfeed();
|
|
const feed = await smartfeed.parseFeedFromString(feedXml);
|
|
return FeedImporter.mapItems(feed.items, options);
|
|
}
|
|
|
|
/**
|
|
* Import from a feed URL, returning news sitemap URL entries.
|
|
*/
|
|
static async fromUrlAsNews(
|
|
feedUrl: string,
|
|
publicationName: string,
|
|
publicationLanguage?: string,
|
|
options?: interfaces.IFeedImportOptions,
|
|
): Promise<interfaces.ISitemapUrl[]> {
|
|
const smartfeed = new plugins.smartfeed.Smartfeed();
|
|
const feed = await smartfeed.parseFeedFromUrl(feedUrl);
|
|
return FeedImporter.mapItemsAsNews(feed.items, publicationName, publicationLanguage ?? 'en', options);
|
|
}
|
|
|
|
/**
|
|
* Import from a feed string, returning news sitemap URL entries.
|
|
*/
|
|
static async fromStringAsNews(
|
|
feedXml: string,
|
|
publicationName: string,
|
|
publicationLanguage?: string,
|
|
options?: interfaces.IFeedImportOptions,
|
|
): Promise<interfaces.ISitemapUrl[]> {
|
|
const smartfeed = new plugins.smartfeed.Smartfeed();
|
|
const feed = await smartfeed.parseFeedFromString(feedXml);
|
|
return FeedImporter.mapItemsAsNews(feed.items, publicationName, publicationLanguage ?? 'en', options);
|
|
}
|
|
|
|
/**
|
|
* Map parsed feed items to standard sitemap URLs.
|
|
*/
|
|
private static mapItems(
|
|
items: any[],
|
|
options?: interfaces.IFeedImportOptions,
|
|
): interfaces.ISitemapUrl[] {
|
|
let filtered = FeedImporter.filterItems(items, options);
|
|
|
|
if (options?.mapItem) {
|
|
const results: interfaces.ISitemapUrl[] = [];
|
|
for (const item of filtered) {
|
|
const mapped = options.mapItem(item as interfaces.IFeedItem);
|
|
if (mapped) results.push(mapped);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
return filtered
|
|
.filter((item: any) => item.link)
|
|
.map((item: any) => {
|
|
const url: interfaces.ISitemapUrl = {
|
|
loc: item.link,
|
|
};
|
|
if (item.isoDate) {
|
|
url.lastmod = item.isoDate;
|
|
}
|
|
return url;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Map parsed feed items to news sitemap URLs.
|
|
*/
|
|
private static mapItemsAsNews(
|
|
items: any[],
|
|
publicationName: string,
|
|
publicationLanguage: string,
|
|
options?: interfaces.IFeedImportOptions,
|
|
): interfaces.ISitemapUrl[] {
|
|
let filtered = FeedImporter.filterItems(items, options);
|
|
|
|
if (options?.mapItem) {
|
|
const results: interfaces.ISitemapUrl[] = [];
|
|
for (const item of filtered) {
|
|
const mapped = options.mapItem(item as interfaces.IFeedItem);
|
|
if (mapped) results.push(mapped);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
return filtered
|
|
.filter((item: any) => item.link)
|
|
.map((item: any) => {
|
|
const url: interfaces.ISitemapUrl = {
|
|
loc: item.link,
|
|
news: {
|
|
publication: {
|
|
name: publicationName,
|
|
language: publicationLanguage,
|
|
},
|
|
publicationDate: item.isoDate || new Date().toISOString(),
|
|
title: item.title || '',
|
|
keywords: item.categories,
|
|
},
|
|
};
|
|
if (item.isoDate) {
|
|
url.lastmod = item.isoDate;
|
|
}
|
|
return url;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Apply date and limit filters to feed items.
|
|
*/
|
|
private static filterItems(items: any[], options?: interfaces.IFeedImportOptions): any[] {
|
|
let result = [...items];
|
|
|
|
// Filter by date
|
|
if (options?.newerThan != null) {
|
|
const threshold = options.newerThan instanceof Date
|
|
? options.newerThan.getTime()
|
|
: options.newerThan;
|
|
|
|
result = result.filter((item: any) => {
|
|
if (!item.isoDate) return true; // keep items without dates
|
|
return new Date(item.isoDate).getTime() >= threshold;
|
|
});
|
|
}
|
|
|
|
// Apply limit
|
|
if (options?.limit != null && options.limit > 0) {
|
|
result = result.slice(0, options.limit);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|