Skip to content

Commit

Permalink
refactor(nunjucks): dedicated nunjucks renderer (#4356)
Browse files Browse the repository at this point in the history
* refactor(nunjucks): dedicated nunjucks renderer
* test(nunjucks): improved toArray test
* test(nunjucks): fix render from path failed on windows
* feat(nunjucks): add a filter called safeDump
* refactor(nunjucks): uses Object.values for toArray
* fix(nunjucks): port from theme-next
* fix(nunjucks): add template path to env
* refactor(nunjucks): fix data.path & change filter name
  • Loading branch information
SukkaW committed Jul 7, 2020
1 parent 639199e commit 47b97bf
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/extend/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const { stripIndent } = require('hexo-util');
const { cyan } = require('chalk');
const { Environment } = require('hexo-renderer-nunjucks');
const { Environment } = require('nunjucks');
const Promise = require('bluebird');
const placeholder = '\uFFFC';
const rPlaceholder = /(?:<|&lt;)!--\uFFFC(\d+)--(?:>|&gt;)/g;
Expand Down
8 changes: 5 additions & 3 deletions lib/plugins/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ module.exports = ctx => {

renderer.register('json', 'json', require('./json'), true);

const nunjucks = require('hexo-renderer-nunjucks');
nunjucks.register(ctx);

const yaml = require('./yaml');

renderer.register('yml', 'json', yaml, true);
renderer.register('yaml', 'json', yaml, true);

const nunjucks = require('./nunjucks');

renderer.register('njk', 'html', nunjucks, true);
renderer.register('j2', 'html', nunjucks, true);
};
70 changes: 70 additions & 0 deletions lib/plugins/renderer/nunjucks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

'use strict';

const nunjucks = require('nunjucks');
const { readFileSync } = require('hexo-fs');
const { dirname } = require('path');

function toArray(value) {
if (Array.isArray(value)) {
// Return if given value is an Array
return value;
} else if (typeof value.toArray === 'function') {
return value.toArray();
} else if (value instanceof Map) {
const arr = [];
value.forEach(v => arr.push(v));
return arr;
} else if (value instanceof Set || typeof value === 'string') {
return [...value];
} else if (typeof value === 'object' && value instanceof Object && Boolean(value)) {
return Object.values(value);
}

return [];
}

function safeJsonStringify(json, spacer = undefined) {
if (typeof json !== 'undefined' && json !== null) {
return JSON.stringify(json, null, spacer);
}

return '""';
}

const nunjucksCfg = {
autoescape: false,
throwOnUndefined: false,
trimBlocks: false,
lstripBlocks: false
};

const nunjucksAddFilter = env => {
env.addFilter('toarray', toArray);
env.addFilter('safedump', safeJsonStringify);
};

function njkCompile(data) {
let env;
if (data.path) {
env = nunjucks.configure(dirname(data.path), nunjucksCfg);
} else {
env = nunjucks.configure(nunjucksCfg);
}
nunjucksAddFilter(env);

const text = 'text' in data ? data.text : readFileSync(data.path);

return nunjucks.compile(text, env, data.path);
}

function njkRenderer(data, locals) {
return njkCompile(data).render(locals);
}

njkRenderer.compile = data => {
// Need a closure to keep the compiled template.
return locals => njkCompile(data).render(locals);
};

module.exports = njkRenderer;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
"hexo-fs": "^3.1.0",
"hexo-i18n": "^1.0.0",
"hexo-log": "^2.0.0",
"hexo-renderer-nunjucks": "^2.0.0",
"hexo-util": "^2.0.0",
"js-yaml": "^3.12.0",
"micromatch": "^4.0.2",
"moment": "^2.22.2",
"moment-timezone": "^0.5.21",
"nunjucks": "^3.2.1",
"pretty-hrtime": "^1.0.3",
"resolve": "^1.8.1",
"strip-ansi": "^6.0.0",
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/hello.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello {{ name }}!
1 change: 1 addition & 0 deletions test/scripts/renderers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ describe('Renderers', () => {
require('./json');
require('./plain');
require('./yaml');
require('./nunjucks');
});
226 changes: 226 additions & 0 deletions test/scripts/renderers/nunjucks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
'use strict';

require('chai').should();
const r = require('../../../lib/plugins/renderer/nunjucks');
const { dirname, join } = require('path');

describe('nunjucks', () => {
const fixturePath = join(dirname(dirname(__dirname)), 'fixtures', 'hello.njk');

it('render from string', () => {
const body = [
'Hello {{ name }}!'
].join('\n');

r({ text: body }, {
name: 'world'
}).should.eql('Hello world!');
});

it('render from path', () => {
r({ path: fixturePath }, {
name: 'world'
}).should.matches(/^Hello world!\s*$/);
});

it('compile from text', () => {
const body = [
'Hello {{ name }}!'
].join('\n');

const render = r.compile({
text: body
});

render({
name: 'world'
}).should.eql('Hello world!');
});

it('compile from an .njk file', () => {
const render = r.compile({
path: fixturePath
});

render({
name: 'world'
}).should.eql('Hello world!\n');
});

describe('nunjucks filters', () => {
const forLoop = [
'{% for x in arr | toarray %}',
'{{ x }}',
'{% endfor %}'
].join('');

it('toarray can iterate on Warehouse collections', () => {
const data = {
arr: {
toArray() {
return [1, 2, 3];
}
}
};

r({ text: forLoop }, data).should.eql('123');
});

it('toarray can iterate on plain array', () => {
const data = {
arr: [1, 2, 3]
};

r({ text: forLoop }, data).should.eql('123');
});

it('toarray can iterate on string', () => {
const data = {
arr: '123'
};

r({ text: forLoop }, data).should.eql('123');
});

// https://github.com/lodash/lodash/blob/master/test/toarray.test.js
it('toarray can iterate on objects', () => {
const data = {
arr: { a: '1', b: '2', c: '3' }
};

r({ text: forLoop }, data).should.eql('123');
});

it('toarray can iterate on object string', () => {
const data = {
arr: Object('123')
};

r({ text: forLoop }, data).should.eql('123');
});

it('toarray can iterate on Map', () => {
const data = {
arr: new Map()
};

data.arr.set('a', 1);
data.arr.set('b', 2);
data.arr.set('c', 3);

r({ text: forLoop }, data).should.eql('123');
});

it('toarray can iterate on Set', () => {
const data = {
arr: new Set()
};

data.arr.add(1);
data.arr.add(2);
data.arr.add(3);

r({ text: forLoop }, data).should.eql('123');
});

it('safedump undefined', () => {
const text = [
'{{ items | safedump }}'
].join('\n');

r({ text }).should.eql('""');
});

it('safedump null', () => {
const text = [
'{% set items = null %}',
'{{ items | safedump }}'
].join('\n');

r({ text }).should.eql('\n""');
});

// Adapt from nunjucks test cases
// https://github.com/mozilla/nunjucks/blob/9a0ce364effd28fcdb3ab922fcffa9343b7b3630/tests/filters.js#L98
it('safedump default', () => {
const text = [
'{% set items = ["a", 1, { b : true}] %}',
'{{ items | safedump }}'
].join('\n');

r({ text }).should.eql('\n["a",1,{"b":true}]');
});

it('safedump spacer - 2', () => {
const text = [
'{% set items = ["a", 1, { b : true}] %}',
'{{ items | safedump(2) }}'
].join('\n');

r({ text }).should.eql([
'',
'[',
' "a",',
' 1,',
' {',
' "b": true',
' }',
']'
].join('\n'));
});

it('safedump spacer - 2', () => {
const text = [
'{% set items = ["a", 1, { b : true}] %}',
'{{ items | safedump(2) }}'
].join('\n');

r({ text }).should.eql([
'',
'[',
' "a",',
' 1,',
' {',
' "b": true',
' }',
']'
].join('\n'));
});

it('safedump spacer - 4', () => {
const text = [
'{% set items = ["a", 1, { b : true}] %}',
'{{ items | safedump(4) }}'
].join('\n');

r({ text }).should.eql([
'',
'[',
' "a",',
' 1,',
' {',
' "b": true',
' }',
']'
].join('\n'));
});

it('safedump spacer - \\t', () => {
const text = [
'{% set items = ["a", 1, { b : true}] %}',
'{{ items | safedump(\'\t\') }}'
].join('\n');

r({ text }).should.eql([
'',
'[',
'\t"a",',
'\t1,',
'\t{',
'\t\t"b": true',
'\t}',
']'
].join('\n'));
});
});
});

0 comments on commit 47b97bf

Please sign in to comment.