Skip to content

Commit a0cca0b

Browse files
authored
Merge pull request #96 from curbengh/autodiscovery
feat: add rss autodiscovery
2 parents af62b8e + 77c95f9 commit a0cca0b

File tree

4 files changed

+127
-1
lines changed

4 files changed

+127
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ feed:
3434
content_limit_delim: ' '
3535
order_by: -date
3636
icon: icon.png
37+
autodiscovery: true
3738
```
3839
3940
- **type** - Feed type. (atom/rss2)
@@ -45,3 +46,5 @@ feed:
4546
- **content_limit_delim** - (optional) If **content_limit** is used to shorten post contents, only cut at the last occurrence of this delimiter before reaching the character limit. Not used by default.
4647
- **order_by** - Feed order-by. (Default: -date)
4748
- **icon** - (optional) Custom feed icon. Defaults to a gravatar of email specified in the main config.
49+
- **autodiscovery** - Add feed [autodiscovery](http://www.rssboard.org/rss-autodiscovery). (Default: `true`)
50+
* Many themes already offer this feature, so you may also need to adjust the theme's config if you wish to disable it.

index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const config = hexo.config.feed = Object.assign({
1010
content: true,
1111
content_limit: 140,
1212
content_limit_delim: '',
13-
order_by: '-date'
13+
order_by: '-date',
14+
autodiscovery: true
1415
}, hexo.config.feed);
1516

1617
const type = config.type.toLowerCase();
@@ -33,3 +34,7 @@ if (!extname(config.path)) {
3334
}
3435

3536
hexo.extend.generator.register('feed', require('./lib/generator'));
37+
38+
if (config.autodiscovery === true) {
39+
hexo.extend.filter.register('after_render:html', require('./lib/autodiscovery'));
40+
}

lib/autodiscovery.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
const { url_for } = require('hexo-util');
4+
5+
function autodiscoveryInject(data) {
6+
const { config } = this;
7+
const { feed } = config;
8+
const type = feed.type.replace(/2$/, '');
9+
10+
if (!feed.autodiscovery
11+
|| data.match(/type=['|"]?application\/(atom|rss)\+xml['|"]?/i)) return;
12+
13+
const autodiscoveryTag = `<link rel="alternate" href="${url_for.call(this, feed.path)}" title="${config.title}" type="application/${type}+xml">`;
14+
15+
return data.replace(/<head>(?!<\/head>).+?<\/head>/, (str) => str.replace('</head>', `${autodiscoveryTag}</head>`));
16+
}
17+
18+
module.exports = autodiscoveryInject;

test/index.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,103 @@ describe('Feed generator', () => {
235235
checkURL('http://localhost/', '/', 'item:nth-of-type(3)>enclosure');
236236
});
237237
});
238+
239+
describe('Autodiscovery', () => {
240+
const hexo = new Hexo();
241+
const autoDiscovery = require('../lib/autodiscovery').bind(hexo);
242+
hexo.config.title = 'foo';
243+
hexo.config.feed = {
244+
type: 'atom',
245+
path: 'atom.xml',
246+
autodiscovery: true
247+
};
248+
249+
it('default', () => {
250+
const content = '<head><link></head>';
251+
const result = autoDiscovery(content);
252+
253+
const $ = cheerio.load(result);
254+
$('link[type="application/atom+xml"]').length.should.eql(1);
255+
$('link[type="application/atom+xml"]').attr('href').should.eql('/' + hexo.config.feed.path);
256+
$('link[type="application/atom+xml"]').attr('title').should.eql(hexo.config.title);
257+
258+
result.should.eql('<head><link><link rel="alternate" href="/atom.xml" title="foo" type="application/atom+xml"></head>');
259+
});
260+
261+
it('prepend root', () => {
262+
hexo.config.root = '/root/';
263+
const content = '<head><link></head>';
264+
const result = autoDiscovery(content);
265+
266+
const $ = cheerio.load(result);
267+
$('link[type="application/atom+xml"]').attr('href').should.eql(hexo.config.root + hexo.config.feed.path);
268+
269+
result.should.eql('<head><link><link rel="alternate" href="/root/atom.xml" title="foo" type="application/atom+xml"></head>');
270+
hexo.config.root = '/';
271+
});
272+
273+
it('disable autodiscovery', () => {
274+
hexo.config.feed.autodiscovery = false;
275+
const content = '<head><link></head>';
276+
const result = autoDiscovery(content);
277+
278+
const resultType = typeof result;
279+
resultType.should.eql('undefined');
280+
hexo.config.feed.autodiscovery = true;
281+
});
282+
283+
it('no duplicate tag', () => {
284+
const content = '<head><link>'
285+
+ '<link rel="alternate" href="/atom.xml" title="foo" type="application/atom+xml"></head>';
286+
const result = autoDiscovery(content);
287+
288+
const resultType = typeof result;
289+
resultType.should.eql('undefined');
290+
});
291+
292+
it('ignore empty head tag', () => {
293+
const content = '<head></head>'
294+
+ '<head><link></head>'
295+
+ '<head></head>';
296+
const result = autoDiscovery(content);
297+
298+
const $ = cheerio.load(result);
299+
$('link[type="application/atom+xml"]').length.should.eql(1);
300+
301+
const expected = '<head></head>'
302+
+ '<head><link><link rel="alternate" href="/atom.xml" title="foo" type="application/atom+xml"></head>'
303+
+ '<head></head>';
304+
result.should.eql(expected);
305+
});
306+
307+
it('apply to first non-empty head tag only', () => {
308+
const content = '<head></head>'
309+
+ '<head><link></head>'
310+
+ '<head><link></head>';
311+
const result = autoDiscovery(content);
312+
313+
const $ = cheerio.load(result);
314+
$('link[type="application/atom+xml"]').length.should.eql(1);
315+
316+
const expected = '<head></head>'
317+
+ '<head><link><link rel="alternate" href="/atom.xml" title="foo" type="application/atom+xml"></head>'
318+
+ '<head><link></head>';
319+
result.should.eql(expected);
320+
});
321+
322+
it('rss2', () => {
323+
hexo.config.feed = {
324+
type: 'rss2',
325+
path: 'rss2.xml',
326+
autodiscovery: true
327+
};
328+
const content = '<head><link></head>';
329+
const result = autoDiscovery(content);
330+
331+
const $ = cheerio.load(result);
332+
$('link[rel="alternate"]').attr('type').should.eql('application/rss+xml');
333+
334+
result.should.eql('<head><link><link rel="alternate" href="/rss2.xml" title="foo" type="application/rss+xml"></head>');
335+
});
336+
337+
});

0 commit comments

Comments
 (0)