-
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(nunjucks): dedicated nunjucks renderer (#4356)
* 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
Showing
7 changed files
with
305 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Hello {{ name }}! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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')); | ||
}); | ||
}); | ||
}); |