|
1 | 1 | # @ascorbic/feed-loader |
2 | 2 |
|
| 3 | +## 2.0.0 |
| 4 | + |
| 5 | +### Major Changes |
| 6 | + |
| 7 | +- [#85](https://github.com/ascorbic/astro-loaders/pull/85) [`8c0c2a0`](https://github.com/ascorbic/astro-loaders/commit/8c0c2a04b9bc3747c431bcb1b0d27f881c5f152b) Thanks [@ascorbic](https://github.com/ascorbic)! - **BREAKING CHANGE**: Updated underlying feed parser library |
| 8 | + |
| 9 | + This release updates the underlying feed parsing library from the previous parser to `@rowanmanning/feed-parser`, which provides more robust and standardized feed parsing. There is a legacy mode for the previous data shape. This change includes several breaking changes to the data structure: |
| 10 | + |
| 11 | + ## Schema Changes |
| 12 | + |
| 13 | + ### Category Structure |
| 14 | + |
| 15 | + - **BREAKING**: Category objects now use `label`, `term`, and `url` fields instead of `name` and `domain` |
| 16 | + - Old: `{ name: string, domain: string | null }` |
| 17 | + - New: `{ label: string, term: string, url: string | null }` |
| 18 | + |
| 19 | + ### Media/Enclosure Structure |
| 20 | + |
| 21 | + - **BREAKING**: Media objects now include additional fields and renamed properties |
| 22 | + - Old: `{ url: string, type: string | null, length: number | null }` |
| 23 | + - New: `{ url: string, image: string | null, title: string | null, length: number | null, type: string | null, mimeType: string | null }` |
| 24 | + |
| 25 | + ### Field Name Changes |
| 26 | + |
| 27 | + - **BREAKING**: `link` field renamed to `url` |
| 28 | + - **BREAKING**: `guid` field renamed to `id` |
| 29 | + - **BREAKING**: Atom `summary` field now maps to `description` (consistent with RSS) |
| 30 | + - **BREAKING**: RSS/Atom `enclosure`/`link[@rel=enclosure]` elements now map to `media` array |
| 31 | + |
| 32 | + ## Error Message Changes |
| 33 | + |
| 34 | + - Updated error messages to match new parser behavior: |
| 35 | + - "Item does not have a guid, skipping" → "Item does not have an id or url, skipping" |
| 36 | + - "Response body is empty" → "Feed response is empty" |
| 37 | + |
| 38 | + ## Benefits |
| 39 | + |
| 40 | + - More robust XML/Atom/RSS parsing |
| 41 | + - Better handling of malformed feeds |
| 42 | + - Standardized data structure across feed types |
| 43 | + - Improved character encoding support |
| 44 | + - More comprehensive category and media handling |
| 45 | + |
| 46 | + ## Legacy Mode Support |
| 47 | + |
| 48 | + To ease migration, this release includes a **temporary legacy mode** that maintains backward compatibility: |
| 49 | + |
| 50 | + ```js |
| 51 | + // Enable legacy mode for backward compatibility |
| 52 | + const loader = feedLoader({ |
| 53 | + url: "https://example.com/feed.xml", |
| 54 | + legacy: true, // Will show deprecation warning |
| 55 | + }); |
| 56 | + ``` |
| 57 | + |
| 58 | + ⚠️ **Legacy mode is deprecated** and will be removed in a future major version. Use it only as a temporary migration aid. |
| 59 | + |
| 60 | + ## Migration Guide |
| 61 | + |
| 62 | + ### Option 1: Use Legacy Mode (Temporary) |
| 63 | + |
| 64 | + Enable legacy mode to maintain the old data structure while you plan your migration: |
| 65 | + |
| 66 | + ```js |
| 67 | + const loader = feedLoader({ |
| 68 | + url: "https://example.com/feed.xml", |
| 69 | + legacy: true, |
| 70 | + }); |
| 71 | + // Data will be in the old format with categories[].name, enclosures, link, guid |
| 72 | + ``` |
| 73 | + |
| 74 | + ### Option 2: Update to New Format (Recommended) |
| 75 | + |
| 76 | + Update your code to handle the new structured data format: |
| 77 | + |
| 78 | + #### Field Name Changes |
| 79 | + |
| 80 | + ```js |
| 81 | + // Item fields |
| 82 | + item.link → item.url |
| 83 | + item.guid → item.id |
| 84 | + item.pubdate/item.date → item.published |
| 85 | + item.summary → item.description (Atom feeds) |
| 86 | + item.enclosures → item.media |
| 87 | + ``` |
| 88 | + |
| 89 | + #### Author Structure Change |
| 90 | + |
| 91 | + ```js |
| 92 | + // Old: Single string format |
| 93 | + item.author = "email (name)"; |
| 94 | + |
| 95 | + // New: Array of objects |
| 96 | + item.authors = [{ email: "email", name: "name" }]; |
| 97 | + // Access: item.authors[0]?.name, item.authors[0]?.email |
| 98 | + ``` |
| 99 | + |
| 100 | + #### Category Structure Change |
| 101 | + |
| 102 | + ```js |
| 103 | + // Old: Array of strings |
| 104 | + item.categories = ["category1", "category2"]; |
| 105 | + |
| 106 | + // New: Array of objects |
| 107 | + item.categories = [{ label: "category1", term: "category1", url: null }]; |
| 108 | + // Access: item.categories[0].label |
| 109 | + ``` |
| 110 | + |
| 111 | + #### Media/Enclosure Structure Change |
| 112 | + |
| 113 | + ```js |
| 114 | + // Old: Basic enclosure format |
| 115 | + item.enclosures = [ |
| 116 | + { |
| 117 | + url: "http://example.com/file.mp3", |
| 118 | + type: "audio/mpeg", |
| 119 | + length: "1234", |
| 120 | + }, |
| 121 | + ]; |
| 122 | + |
| 123 | + // New: Enhanced media format |
| 124 | + item.media = [ |
| 125 | + { |
| 126 | + url: "http://example.com/file.mp3", |
| 127 | + mimeType: "audio/mpeg", |
| 128 | + length: 1234, |
| 129 | + image: null, |
| 130 | + title: null, |
| 131 | + }, |
| 132 | + ]; |
| 133 | + ``` |
| 134 | + |
| 135 | + #### Image Structure Change |
| 136 | + |
| 137 | + ```js |
| 138 | + // Old: Simple object with undefined for missing values |
| 139 | + item.image = { url: "http://example.com/image.jpg", title: undefined }; |
| 140 | + |
| 141 | + // New: Full object structure |
| 142 | + item.image = { |
| 143 | + url: "http://example.com/image.jpg", |
| 144 | + title: "Image Title", |
| 145 | + description: "Image description", |
| 146 | + }; |
| 147 | + ``` |
| 148 | + |
| 149 | + #### Meta Structure Changes |
| 150 | + |
| 151 | + ```js |
| 152 | + // Feed generator changed from string to object |
| 153 | + meta.generator = "WordPress" → feed.generator = { name: "WordPress" } |
| 154 | + |
| 155 | + // Authors follow same pattern as items |
| 156 | + meta.author = "email (name)" → feed.authors = [{ email: "email", name: "name" }] |
| 157 | + ``` |
| 158 | + |
| 159 | + Most users who only access `title`, `description`, `url`, and basic fields will not need changes. |
| 160 | + |
| 161 | +### Minor Changes |
| 162 | + |
| 163 | +- [#88](https://github.com/ascorbic/astro-loaders/pull/88) [`1049d3e`](https://github.com/ascorbic/astro-loaders/commit/1049d3e865299267833012be3baf1bf4bd0cfea5) Thanks [@ascorbic](https://github.com/ascorbic)! - Adds experimental live feed loader |
| 164 | + |
| 165 | + Adds a new `liveFeedLoader` for Astro's experimental live content collections feature. This allows RSS/Atom feeds to be fetched at request time rather than build time, enabling real-time content updates without rebuilds. |
| 166 | + |
| 167 | + **Features:** |
| 168 | + |
| 169 | + - Runtime feed loading with `liveFeedLoader()` |
| 170 | + - Support for RSS, Atom, and RDF feeds |
| 171 | + - Collection filtering (limit, category, author, date ranges) |
| 172 | + - Individual entry loading by ID or URL |
| 173 | + - Structured error handling with `FeedLoadError` and `FeedValidationError` |
| 174 | + - TypeScript support with proper generics |
| 175 | + |
| 176 | + **Requirements:** |
| 177 | + |
| 178 | + - Astro 5.10.0 or later |
| 179 | + - Experimental live content collections enabled in `astro.config.mjs` |
| 180 | + |
| 181 | + **Usage:** |
| 182 | + |
| 183 | + ```typescript |
| 184 | + // src/live.config.ts |
| 185 | + import { defineLiveCollection } from "astro:content"; |
| 186 | + import { liveFeedLoader } from "@ascorbic/feed-loader"; |
| 187 | + |
| 188 | + const news = defineLiveCollection({ |
| 189 | + type: "live", |
| 190 | + loader: liveFeedLoader({ |
| 191 | + url: "https://feeds.example.com/news.xml", |
| 192 | + }), |
| 193 | + }); |
| 194 | + ``` |
| 195 | + |
| 196 | + The existing `feedLoader` remains unchanged and fully compatible. |
| 197 | + |
3 | 198 | ## 1.0.4 |
4 | 199 |
|
5 | 200 | ### Patch Changes |
|
0 commit comments