npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ts-rss-builder

v1.0.4

Published

RSS builder in Typescript, tailored to Astro. For NN1.dev

Readme

ts-rss-builder

A Zero-Dependency TypeScript RSS builder for Astro.

Purpose

Generate RSS feeds from Astro collections with ease. Built for NN1 Spotlights RSS feed.

Why?

For learning and to keep things simple. Most RSS builders have alot of deps and stuff you don't need. My library is zero-dep, easy to understand, and made to play nicely with Astro collections.

Installation

pnpm install ts-rss-builder

Usage

import { getCollection } from 'astro:content';
import { createRSSFromAstroCollection } from 'ts-rss-builder';
import sanitizeHtml from 'sanitize-html';

const baseUrl = "https://example.com";
const blog = await getCollection('blog');

return createRSSFromAstroCollection(blog, {
      site: baseUrl,
      feedUrl: `${baseUrl}/blog.xml`,
      mappers: {
        title: (entry) => entry.data.name,
        link: (entry) => `${baseUrl}/blog/${entry.id}`,
        description: (entry) => generateDescription(entry),	// You'd usually get this from entry.data
        content: (entry) => sanitizeContent(entry.rendered?.html || entry.body || ''), // sanitizeHtml is a custom defined wrapping sanitizeHtml
        pubDate: (entry) => entry.data.date,
        guid: (entry) => `${baseUrl}/blog/${entry.id}`,
        enclosure: (entry) => createImageEnclosure(entry, baseUrl, isDev), // // You'd usually get this from entry.data (image)
      },
});

Configuration Interface

interface AstroRSSConfig<T = any> {
  title: string;                // Feed title
  description: string;          // Feed description
  site: string;                 // Base site URL (e.g. https://example.com)
  feedUrl: string;              // Full URL to the RSS feed (e.g. https://example.com/feed.xml)
  language?: string;            // Optional language code (e.g. "en-GB")
  customData?: string;          // Optional raw XML string for additional RSS tags/namespaces

  mappers: {
    title: (entry: T) => string;              // Map entry to item title
    link: (entry: T) => string;               // Map entry to item link
    description: (entry: T) => string;        // Map entry to item description (typically summary)
    content: (entry: T) => string;             // Map entry to item full content (for content:encoded)
    pubDate: (entry: T) => string | Date;     // Map entry to publication date (ISO string or Date)
    category?: (entry: T) => string | string[] | undefined; // Optional categories/tags
    guid?: (entry: T) => string;               // Optional unique identifier for the item
    enclosure?: (entry: T) => RSSEnclosure | undefined;    // Optional media enclosure
  };
}

interface RSSEnclosure {
  url: string;
  length?: number | string;
  type?: string; // MIME type, e.g. "image/jpeg"
}

Security, XML Safety & Compliance

This builder is designed with XML correctness and security in mind. Here are some techniques it uses:

XML Character Escaping:

  • All user-supplied text (titles, descriptions, links, etc.) is escaped to replace characters like &, <, >, ", and ' with their XML entity equivalents (&, <, etc.). -This prevents XML injection, broken markup, and parsing errors.

Safe CDATA Handling:

  • Content inside <content:encoded> is wrapped in CDATA sections to allow HTML without heavy escaping.
  • This library escapes any ]]> sequences inside content to ]]>, preventing early CDATA section termination, which would corrupt the XML.
  • It also removes dangerous control characters (non-printable ASCII) which can cause XML parsers to fail or behave unexpectedly.

Date Formatting:

  • Publication dates are strictly formatted to RFC-822 compliant strings, ensuring compatibility with RSS readers and feed validators.
  • Enclosure & Category Safety

Caching:

  • XML escaping results are cached for performance when escaping repeated strings.