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'
1
11
import { u } from 'unist-builder'
2
12
import { x } from 'xastscript'
3
13
import 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
+ */
6
24
export function rss ( channel , data ) {
7
25
var now = new Date ( )
8
- var meta = channel || { }
26
+ /** @type {Channel } */
27
+ var meta = channel || { title : null , url : null }
28
+ /** @type {Array.<Element> } */
9
29
var items = [ ]
10
30
var index = - 1
31
+ /** @type {boolean } */
11
32
var atom
33
+ /** @type {number } */
12
34
var offset
35
+ /** @type {Array.<Element> } */
13
36
var children
37
+ /** @type {Entry } */
14
38
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
16
49
17
50
if ( meta . title == null ) throw new Error ( 'Expected `channel.title` to be set' )
18
51
if ( meta . url == null ) throw new Error ( 'Expected `channel.url` to be set' )
@@ -21,6 +54,7 @@ export function rss(channel, data) {
21
54
x ( 'title' , String ( meta . title ) ) ,
22
55
x ( 'description' , String ( meta . description || '' ) || null ) ,
23
56
x ( 'link' , new URL ( meta . url ) . href ) ,
57
+ // @ts -ignore `toGTMString` is exactly what we need.
24
58
x ( 'lastBuildDate' , now . toGMTString ( ) ) ,
25
59
x ( 'dc:date' , now . toISOString ( ) )
26
60
)
@@ -37,13 +71,13 @@ export function rss(channel, data) {
37
71
}
38
72
39
73
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 ) )
42
76
}
43
77
44
78
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 ) )
47
81
}
48
82
49
83
if ( meta . tags ) {
@@ -69,41 +103,36 @@ export function rss(channel, data) {
69
103
if ( datum . title ) children . push ( x ( 'title' , String ( datum . title ) ) )
70
104
71
105
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 ) )
74
108
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 + ')' ) )
77
111
}
78
112
}
79
113
80
114
if ( datum . url ) {
81
- value = new URL ( datum . url ) . href
115
+ url = new URL ( datum . url ) . href
82
116
children . push (
83
- x ( 'link' , value ) ,
117
+ x ( 'link' , url ) ,
84
118
// Do not treat it as a URL, just an opaque identifier.
85
119
// `<link>` is already used by readers for the URL.
86
120
// Now, the value we have here is a URL, but we can’t know if it’s
87
121
// “permanent”, so, set `false`.
88
- x ( 'guid' , { isPermaLink : 'false' } , value )
122
+ x ( 'guid' , { isPermaLink : 'false' } , url )
89
123
)
90
124
}
91
125
92
- value = datum . published
93
-
94
- if ( value != null ) {
95
- if ( typeof value !== 'object' ) value = new Date ( value )
126
+ if ( datum . published != null ) {
96
127
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 ( ) )
99
131
)
100
132
}
101
133
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 ( ) ) )
107
136
}
108
137
109
138
if ( datum . tags ) {
@@ -113,23 +142,23 @@ export function rss(channel, data) {
113
142
}
114
143
}
115
144
116
- value = datum . enclosure
117
- if ( value ) {
118
- if ( ! value . url ) {
145
+ enclosure = datum . enclosure
146
+ if ( enclosure ) {
147
+ if ( ! enclosure . url ) {
119
148
throw new Error (
120
149
'Expected either `enclosure.url` to be set in entry `' + index + '`'
121
150
)
122
151
}
123
152
124
- if ( ! value . size ) {
153
+ if ( ! enclosure . size ) {
125
154
throw new Error (
126
155
'Expected either `enclosure.size` to be set in entry `' +
127
156
index +
128
157
'`'
129
158
)
130
159
}
131
160
132
- if ( ! value . type ) {
161
+ if ( ! enclosure . type ) {
133
162
throw new Error (
134
163
'Expected either `enclosure.type` to be set in entry `' +
135
164
index +
@@ -138,16 +167,13 @@ export function rss(channel, data) {
138
167
}
139
168
140
169
// 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
+ )
151
177
}
152
178
153
179
if ( datum . descriptionHtml || datum . description ) {
0 commit comments