Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add generate sitemap.txt #136

Merged
merged 4 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ You can configure this plugin in `_config.yml`.

``` yaml
sitemap:
path: sitemap.xml
path:
- sitemap.xml
- sitemap.txt
template: ./sitemap_template.xml
template_txt: ./sitemap_template.txt
rel: false
tags: true
categories: true
```

- **path** - Sitemap path. (Default: sitemap.xml)
- **template** - Custom template path. This file will be used to generate sitemap.xml (See [default template](/sitemap.xml))
- **template** - Custom template path. This file will be used to generate sitemap.xml (See [default xml template](/sitemap.xml))
- **template_txt** - Custom template path. This file will be used to generate sitemap.txt (See [default txt template](/sitemap.txt))
- **rel** - Add [`rel-sitemap`](http://microformats.org/wiki/rel-sitemap) to the site's header. (Default: `false`)
- **tags** - Add site's tags
- **categories** - Add site's categories
Expand Down
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
const { extname } = require('path');

hexo.config.sitemap = Object.assign({
path: 'sitemap.xml',
path: ['sitemap.xml', 'sitemap.txt'],
rel: false,
tags: true,
categories: true
}, hexo.config.sitemap);

const config = hexo.config.sitemap;

if (!extname(config.path)) {
if (typeof config.path === 'string' && !extname(config.path)) {
config.path += '.xml';
}

Expand Down
25 changes: 12 additions & 13 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const template = require('./template');
module.exports = function(locals) {
const { config } = this;
const { sitemap, skip_render } = config;
const { path, tags: tagsCfg, categories: catsCfg } = sitemap;
const { tags: tagsCfg, categories: catsCfg } = sitemap;
const skipRenderList = [
'**/*.js',
'**/*.css'
Expand All @@ -33,18 +33,17 @@ module.exports = function(locals) {
return;
}

const data = template(config).render({
config,
posts,
sNow: new Date(),
tags: tagsCfg ? locals.tags.toArray() : [],
categories: catsCfg ? locals.categories.toArray() : []
});

return {
path,
data
};
const res = template(config);
for (const i in res) {
res[i].data = res[i].data.render({
config,
posts,
sNow: new Date(),
tags: tagsCfg ? locals.tags.toArray() : [],
categories: catsCfg ? locals.categories.toArray() : []
});
}
return res;
};

function isMatch(path, patterns) {
Expand Down
33 changes: 27 additions & 6 deletions lib/template.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
'use strict';

const { join } = require('path');
const { join, extname } = require('path');
const { readFileSync } = require('fs');
let sitemapTmpl;
const { encodeURL } = require('hexo-util');

module.exports = function(config) {
if (sitemapTmpl) return sitemapTmpl;

const path = config.sitemap.path;
const nunjucks = require('nunjucks');
const env = new nunjucks.Environment(null, {
autoescape: false,
Expand All @@ -23,8 +22,30 @@ module.exports = function(config) {
return input.toISOString().substring(0, 10);
});

const sitemapSrc = config.sitemap.template || join(__dirname, '../sitemap.xml');
sitemapTmpl = nunjucks.compile(readFileSync(sitemapSrc, 'utf8'), env);
function temp(p) {
let sitemapSrc;
switch (extname(p)) {
case '.xml':
sitemapSrc = config.sitemap.template || join(__dirname, '../sitemap.xml');
break;
case '.txt':
sitemapSrc = config.sitemap.template_txt || join(__dirname, '../sitemap.txt');
break;
}
sitemapTmpl = nunjucks.compile(readFileSync(sitemapSrc, 'utf8'), env);
return { path: p, data: sitemapTmpl };
}

return sitemapTmpl;
const res = [];
switch (typeof path) {
case 'string':
res.push(temp(path));
break;
case 'object':
for (const p of path) {
res.push(temp(p));
}
break;
}
return res;
};
5 changes: 5 additions & 0 deletions sitemap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% for post in posts %}{{ post.permalink | uriencode }}
{% endfor %}{{ config.url | uriencode }}
{% for tag in tags %}{{ tag.permalink | uriencode }}
{% endfor %}{% for cat in categories %}{{ cat.permalink | uriencode }}
{% endfor %}
112 changes: 93 additions & 19 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ const Hexo = require('hexo');
const cheerio = require('cheerio');
const { deepMerge, encodeURL } = require('hexo-util');
const { transform } = require('camaro');
const { extname } = require('path');
const sitemapCfg = {
path: 'sitemap.xml',
path: ['sitemap.xml', 'sitemap.txt'],
rel: false,
tags: true,
categories: true
Expand All @@ -31,8 +32,37 @@ describe('Sitemap generator', () => {

const Post = hexo.model('Post');
const Page = hexo.model('Page');
const generator = require('../lib/generator').bind(hexo);
const sitemapTmpl = require('../lib/template')(hexo.config);
const templ = require('../lib/template')(hexo.config);
const generator = function(locals) {
const res = require('../lib/generator').bind(hexo)(locals);
const data = {};
for (const r of res) {
switch (extname(r.path)) {
case '.xml':
data.xml = r;
break;
case '.txt':
data.txt = r;
break;
}
}
return data;
};
const sitemapTmpl = (function(templ) {
const data = {};
for (const r of templ) {
switch (extname(r.path)) {
case '.xml':
data.xml = r.data;
break;
case '.txt':
data.txt = r.data;
break;
}
}
return data;
}(templ));

let posts = [];
let locals = {};

Expand Down Expand Up @@ -60,11 +90,11 @@ describe('Sitemap generator', () => {
});

it('default', async () => {
const result = generator(locals);
const result = generator(locals).xml;
const { items } = await p(result.data);

result.path.should.eql('sitemap.xml');
result.data.should.eql(sitemapTmpl.render({
result.data.should.eql(sitemapTmpl.xml.render({
config: hexo.config,
posts,
sNow: new Date(),
Expand All @@ -79,7 +109,7 @@ describe('Sitemap generator', () => {
});

it('tags', async () => {
const { data } = generator(locals);
const { data } = generator(locals).xml;
const { items } = await p(data);

const result = items.filter(({ link }) => link.includes('tags'));
Expand All @@ -90,7 +120,7 @@ describe('Sitemap generator', () => {

it('tags - disable', async () => {
hexo.config.sitemap.tags = false;
const { data } = generator(locals);
const { data } = generator(locals).xml;
const { items } = await p(data);

const result = items.filter(({ link }) => link.includes('tags'));
Expand All @@ -99,7 +129,7 @@ describe('Sitemap generator', () => {
});

it('categories', async () => {
const { data } = generator(locals);
const { data } = generator(locals).xml;
const { items } = await p(data);

const result = items.filter(({ link }) => link.includes('categories'));
Expand All @@ -110,7 +140,7 @@ describe('Sitemap generator', () => {

it('categories - disable', async () => {
hexo.config.sitemap.categories = false;
const { data } = generator(locals);
const { data } = generator(locals).xml;
const { items } = await p(data);

const result = items.filter(({ link }) => link.includes('categories'));
Expand All @@ -122,31 +152,49 @@ describe('Sitemap generator', () => {
it('array', () => {
hexo.config.skip_render = ['foo'];

const result = generator(locals);
const result = generator(locals).xml;
result.data.should.not.contain('foo');
});

it('string', () => {
hexo.config.skip_render = 'bar';

const result = generator(locals);
const result = generator(locals).xml;
result.data.should.not.contain('bar');
});

it('string - off', () => { // coverage branch 100%
hexo.config.skip_render = '';

const result = generator(locals).xml;
result.should.be.ok;
});

it('invalid type', () => {
hexo.config.skip_render = { foo: 'bar' };

const result = generator(locals);
const result = generator(locals).xml;
result.should.be.ok;
});

it('off', () => {
hexo.config.skip_render = null;

const result = generator(locals);
const result = generator(locals).xml;
result.should.be.ok;
});
});

it('Sitemap-TXT', () => {
const result = generator(locals).txt;
const reg = new RegExp('\\r\\n|\\r|\\n', 'g');
let items = result.data.replace(reg, '\n');
items = items.split('\n');
result.path.should.eql('sitemap.txt');
for (let i = 0; i < posts.length; i++) {
items[i].should.eql(posts[i].permalink);
}
});
});

it('No posts', async () => {
Expand Down Expand Up @@ -252,8 +300,21 @@ describe('IDN', () => {
path: 'sitemap.xml'
};
const Post = hexo.model('Post');
const generator = require('../lib/generator').bind(hexo);

const generator = function(locals) {
const res = require('../lib/generator').bind(hexo)(locals);
const data = {};
for (const r of res) {
switch (extname(r.path)) {
case '.xml':
data.xml = r;
break;
case '.txt':
data.txt = r;
break;
}
}
return data;
};
hexo.config.url = 'http://fôo.com/bár';
const parsedUrl = encodeURL(hexo.config.url);

Expand All @@ -263,7 +324,7 @@ describe('IDN', () => {
});
const locals = hexo.locals.toObject();

const result = generator(locals);
const result = generator(locals).xml;
const { items } = await p(result.data);
items.forEach(element => {
element.link.startsWith(parsedUrl).should.eql(true);
Expand All @@ -278,8 +339,21 @@ describe('IDN', () => {
path: 'sitemap.xml'
};
const Post = hexo.model('Post');
const generator = require('../lib/generator').bind(hexo);

const generator = function(locals) {
const res = require('../lib/generator').bind(hexo)(locals);
const data = {};
for (const r of res) {
switch (extname(r.path)) {
case '.xml':
data.xml = r;
break;
case '.txt':
data.txt = r;
break;
}
}
return data;
};
hexo.config.url = 'http://foo.com/b%C3%A1r';

await hexo.init();
Expand All @@ -288,7 +362,7 @@ describe('IDN', () => {
});
const locals = hexo.locals.toObject();

const result = generator(locals);
const result = generator(locals).xml;
const { items } = await p(result.data);
items.forEach(element => {
element.link.startsWith(hexo.config.url).should.eql(true);
Expand Down