1+ /**
2+ * @typedef {import('xast').Element } Element
3+ * @typedef {import('xast').Root } Root
4+ * @typedef {import('./types.js').Author } Author
5+ * @typedef {import('./types.js').Enclosure } Enclosure
6+ * @typedef {import('./types.js').Channel } Channel
7+ * @typedef {import('./types.js').Entry } Entry
8+ */
9+
10+ import { URL } from 'url'
111import { u } from 'unist-builder'
212import { x } from 'xastscript'
313import bcp47 from 'bcp-47-normalize'
4- import { toAuthor } from './util.js'
5-
14+ import { toAuthor , toDate } from './util.js'
15+
16+ /**
17+ * Build an RSS feed.
18+ * Same API as `atom` otherwise.
19+ *
20+ * @param {Channel } channel
21+ * @param {Array.<Entry> } [data]
22+ * @returns {Root }
23+ */
624export function rss ( channel , data ) {
725 var now = new Date ( )
8- var meta = channel || { }
26+ /** @type {Channel } */
27+ var meta = channel || { title : null , url : null }
28+ /** @type {Array.<Element> } */
929 var items = [ ]
1030 var index = - 1
31+ /** @type {boolean } */
1132 var atom
33+ /** @type {number } */
1234 var offset
35+ /** @type {Array.<Element> } */
1336 var children
37+ /** @type {Entry } */
1438 var datum
15- var value
39+ /** @type {string } */
40+ var lang
41+ /** @type {string } */
42+ var copy
43+ /** @type {string } */
44+ var url
45+ /** @type {Author } */
46+ var author
47+ /** @type {Enclosure } */
48+ var enclosure
1649
1750 if ( meta . title == null ) throw new Error ( 'Expected `channel.title` to be set' )
1851 if ( meta . url == null ) throw new Error ( 'Expected `channel.url` to be set' )
@@ -21,6 +54,7 @@ export function rss(channel, data) {
2154 x ( 'title' , String ( meta . title ) ) ,
2255 x ( 'description' , String ( meta . description || '' ) || null ) ,
2356 x ( 'link' , new URL ( meta . url ) . href ) ,
57+ // @ts -ignore `toGTMString` is exactly what we need.
2458 x ( 'lastBuildDate' , now . toGMTString ( ) ) ,
2559 x ( 'dc:date' , now . toISOString ( ) )
2660 )
@@ -37,13 +71,13 @@ export function rss(channel, data) {
3771 }
3872
3973 if ( meta . lang ) {
40- value = bcp47 ( meta . lang )
41- items . push ( x ( 'language' , value ) , x ( 'dc:language' , value ) )
74+ lang = bcp47 ( meta . lang )
75+ items . push ( x ( 'language' , lang ) , x ( 'dc:language' , lang ) )
4276 }
4377
4478 if ( meta . author ) {
45- value = '© ' + now . getUTCFullYear ( ) + ' ' + meta . author
46- items . push ( x ( 'copyright' , value ) , x ( 'dc:rights' , value ) )
79+ copy = '© ' + now . getUTCFullYear ( ) + ' ' + meta . author
80+ items . push ( x ( 'copyright' , copy ) , x ( 'dc:rights' , copy ) )
4781 }
4882
4983 if ( meta . tags ) {
@@ -69,41 +103,36 @@ export function rss(channel, data) {
69103 if ( datum . title ) children . push ( x ( 'title' , String ( datum . title ) ) )
70104
71105 if ( datum . author ) {
72- value = toAuthor ( datum . author )
73- children . push ( x ( 'dc:creator' , value . name ) )
106+ author = toAuthor ( datum . author )
107+ children . push ( x ( 'dc:creator' , author . name ) )
74108
75- if ( value . email ) {
76- children . push ( x ( 'author' , value . email + ' (' + value . name + ')' ) )
109+ if ( author . email ) {
110+ children . push ( x ( 'author' , author . email + ' (' + author . name + ')' ) )
77111 }
78112 }
79113
80114 if ( datum . url ) {
81- value = new URL ( datum . url ) . href
115+ url = new URL ( datum . url ) . href
82116 children . push (
83- x ( 'link' , value ) ,
117+ x ( 'link' , url ) ,
84118 // Do not treat it as a URL, just an opaque identifier.
85119 // `<link>` is already used by readers for the URL.
86120 // Now, the value we have here is a URL, but we can’t know if it’s
87121 // “permanent”, so, set `false`.
88- x ( 'guid' , { isPermaLink : 'false' } , value )
122+ x ( 'guid' , { isPermaLink : 'false' } , url )
89123 )
90124 }
91125
92- value = datum . published
93-
94- if ( value != null ) {
95- if ( typeof value !== 'object' ) value = new Date ( value )
126+ if ( datum . published != null ) {
96127 children . push (
97- x ( 'pubDate' , value . toGMTString ( ) ) ,
98- x ( 'dc:date' , value . toISOString ( ) )
128+ // @ts -ignore `toGTMString` is exactly what we need.
129+ x ( 'pubDate' , toDate ( datum . published ) . toGMTString ( ) ) ,
130+ x ( 'dc:date' , toDate ( datum . published ) . toISOString ( ) )
99131 )
100132 }
101133
102- value = datum . modified
103-
104- if ( value != null ) {
105- if ( typeof value !== 'object' ) value = new Date ( value )
106- children . push ( x ( 'dc:modified' , value . toISOString ( ) ) )
134+ if ( datum . modified != null ) {
135+ children . push ( x ( 'dc:modified' , toDate ( datum . modified ) . toISOString ( ) ) )
107136 }
108137
109138 if ( datum . tags ) {
@@ -113,23 +142,23 @@ export function rss(channel, data) {
113142 }
114143 }
115144
116- value = datum . enclosure
117- if ( value ) {
118- if ( ! value . url ) {
145+ enclosure = datum . enclosure
146+ if ( enclosure ) {
147+ if ( ! enclosure . url ) {
119148 throw new Error (
120149 'Expected either `enclosure.url` to be set in entry `' + index + '`'
121150 )
122151 }
123152
124- if ( ! value . size ) {
153+ if ( ! enclosure . size ) {
125154 throw new Error (
126155 'Expected either `enclosure.size` to be set in entry `' +
127156 index +
128157 '`'
129158 )
130159 }
131160
132- if ( ! value . type ) {
161+ if ( ! enclosure . type ) {
133162 throw new Error (
134163 'Expected either `enclosure.type` to be set in entry `' +
135164 index +
@@ -138,16 +167,13 @@ export function rss(channel, data) {
138167 }
139168
140169 // Can’t use `xastscript` because of `length`.
141- children . push ( {
142- type : 'element' ,
143- name : 'enclosure' ,
144- attributes : {
145- url : new URL ( value . url ) . href ,
146- length : String ( value . size ) ,
147- type : value . type
148- } ,
149- children : [ ]
150- } )
170+ children . push (
171+ x ( 'enclosure' , {
172+ url : new URL ( enclosure . url ) . href ,
173+ length : String ( enclosure . size ) ,
174+ type : enclosure . type
175+ } )
176+ )
151177 }
152178
153179 if ( datum . descriptionHtml || datum . description ) {
0 commit comments