Skip to content

Commit 1f5c9fc

Browse files
authored
resolves #925 configure template engines (#944)
1 parent 51436a0 commit 1f5c9fc

File tree

6 files changed

+165
-9
lines changed

6 files changed

+165
-9
lines changed

docs/modules/extend/pages/converter/template-converter.adoc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,77 @@ An optional JSON of options.
250250

251251
`helpers`::
252252
The functions and values exported from the _helpers.js_ file.
253+
254+
== Template options
255+
256+
You can configure the template engine using the `template_engine_options` option.
257+
Here's a few examples:
258+
259+
[source,js]
260+
----
261+
const options = {
262+
template_engine_options: {
263+
nunjucks: {
264+
autoescape: false
265+
},
266+
handlebars: {
267+
noEscape: true
268+
},
269+
pug: {
270+
doctype: 'xml'
271+
},
272+
ejs: {
273+
delimiter: '?',
274+
openDelimiter: '[',
275+
closeDelimiter: ']'
276+
}
277+
}
278+
}
279+
----
280+
281+
To find out which options you can use, please read the official documentation of your template engine:
282+
283+
EJS::
284+
https://github.com/mde/ejs#options
285+
286+
Handlebars::
287+
https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options
288+
289+
Nunjucks::
290+
https://mozilla.github.io/nunjucks/api.html#constructor
291+
292+
Pug::
293+
https://pugjs.org/api/reference.html#options
294+
295+
== Template cache
296+
297+
For performance reasons, templates are cached, but you can disable this feature using the `template_cache` option:
298+
299+
[source,js]
300+
----
301+
asciidoctor.convert(input, { template_cache: false })
302+
----
303+
304+
It might be useful when you want to configure the same template directory with different options.
305+
In the following example, we want to an XML https://pugjs.org/language/doctype.html[doctype].
306+
We need to disable the cache otherwise the second conversion will not reconfigure the templates with the `doctype: 'xml'` option:
307+
308+
[source,js]
309+
----
310+
const options = {
311+
safe: 'safe',
312+
doctype: 'inline',
313+
backend: 'html5',
314+
template_dir: '/path/to/templates/pug',
315+
template_cache: false, // disable template cache
316+
}
317+
318+
console.log(asciidoctor.convert(`image:cat.png[]`, options)) // <img src="cat.png"/>
319+
console.log(asciidoctor.convert('image:cat.png[]', Object.assign(options, {
320+
template_engine_options: {
321+
pug: {
322+
doctype: 'xml'
323+
}
324+
}
325+
}))) // <img src="cat.png"></img>
326+
----

packages/core/lib/asciidoctor/js/asciidoctor_ext/node/template.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def initialize backend, template_dirs, opts = {}
2020
@templates = {}
2121
@template_dirs = template_dirs
2222
@engine = opts[:template_engine]
23+
@engine_options = opts[:template_engine_options] || {}
2324
case opts[:template_cache]
2425
when true
2526
@caches = self.class.caches
@@ -181,7 +182,9 @@ def scan_dir template_dir, pattern, template_cache = nil
181182
if (enginesContext.nunjucks && enginesContext.nunjucks.environment) {
182183
env = enginesContext.nunjucks.environment
183184
} else {
184-
env = nunjucks.configure(#{template_dir}, {})
185+
var opts = self.engine_options['nunjucks'] || {}
186+
delete opts.web // unsupported option
187+
env = nunjucks.configure(#{template_dir}, opts)
185188
enginesContext.nunjucks = { environment: env }
186189
}
187190
template = Object.assign(nunjucks.compile(fs.readFileSync(file, 'utf8'), env), { '$file': function() { return file } })
@@ -191,24 +194,32 @@ def scan_dir template_dir, pattern, template_cache = nil
191194
%x{
192195
var fs = require('fs')
193196
var env
197+
var opts = self.engine_options['handlebars'] || {}
194198
if (enginesContext.handlebars && enginesContext.handlebars.environment) {
195199
env = enginesContext.handlebars.environment
196200
} else {
197201
env = handlebars.create()
198202
enginesContext.handlebars = { environment: env }
199203
}
200-
template = { render: env.compile(fs.readFileSync(file, 'utf8')), '$file': function() { return file } }
204+
template = { render: env.compile(fs.readFileSync(file, 'utf8'), opts), '$file': function() { return file } }
201205
}
202206
when :ejs
203207
ejs = node_require 'ejs'
204208
%x{
205209
var fs = require('fs')
206-
template = { render: ejs.compile(fs.readFileSync(file, 'utf8')), '$file': function() { return file } }
210+
var opts = self.engine_options['ejs'] || {}
211+
opts.filename = file
212+
// unsupported options
213+
delete opts.async
214+
delete opts.client
215+
template = { render: ejs.compile(fs.readFileSync(file, 'utf8'), opts), '$file': function() { return file } }
207216
}
208217
when :pug
209218
pug = node_require 'pug'
210219
%x{
211-
template = { render: pug.compileFile(file), '$file': function() { return file } }
220+
var opts = self.engine_options['pug'] || {}
221+
opts.filename = file
222+
template = { render: pug.compileFile(file, opts), '$file': function() { return file } }
212223
}
213224
when :js
214225
template = `{ render: require(file), '$file': function() { return file } }`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p class="paragraph-ejs">[?= node.getContent() ?]</p>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
img(src=node.getTarget())
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
p.paragraph-pug #{node.getContent()}
1+
p.paragraph-pug !{node.getContent()}

packages/core/spec/node/asciidoctor.spec.js

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,9 +1570,10 @@ sample content`, opts)
15701570
const opts = { extension_registry: registry }
15711571
const doc = asciidoctor.load('img::image-name[]', opts)
15721572
const images = doc.findBy((b) => b.getContext() === 'image')
1573-
expect(images.length).to.equal(1)
1574-
expect(images[0].getTitle()).to.equal('title')
1575-
expect(images[0].getCaption()).to.equal('caption')
1573+
expect(images).to.have.length(1)
1574+
const firstImage = images[0]
1575+
expect(firstImage.getTitle()).to.equal('title')
1576+
expect(firstImage.getCaption()).to.equal('caption')
15761577
const result = doc.convert(opts)
15771578
expect(result).to.contain('<img src="image-name.png" alt="image name">')
15781579
})
@@ -2759,7 +2760,10 @@ content`
27592760
</article>`)
27602761
templateConverter.register('admonition', template)
27612762
const templates = templateConverter.getTemplates()
2762-
const admonition = asciidoctor.Block.create(doc, 'admonition', { source: 'An admonition paragraph, like this note, grabs the reader’s attention.', attributes: { textlabel: 'Note' } })
2763+
const admonition = asciidoctor.Block.create(doc, 'admonition', {
2764+
source: 'An admonition paragraph, like this note, grabs the reader’s attention.',
2765+
attributes: { textlabel: 'Note' }
2766+
})
27632767
expect(templates.admonition.render({ node: admonition })).to.equal(`<article class="message is-info">
27642768
<div class="message-header">
27652769
<p>Note</p>
@@ -2803,6 +2807,71 @@ And here&#8217;s a block image:</p>
28032807
`, options)
28042808
expect(result.replace(/\r/g, '').replace(/\n/g, '')).to.equal('<ul class="ulist"><p>foo</p><p>bar</p><p>baz</p></ul>')
28052809
}).timeout(5000)
2810+
it('should configure Nunjucks environment using the template_engine_options', () => {
2811+
const options = {
2812+
safe: 'safe',
2813+
backend: 'html5',
2814+
template_dir: 'spec/fixtures/templates/nunjucks',
2815+
template_cache: false, // disable template cache to recreate templates with the given options
2816+
template_engine_options: {
2817+
nunjucks: {
2818+
autoescape: false,
2819+
web: {
2820+
async: true
2821+
}
2822+
}
2823+
}
2824+
}
2825+
const result = asciidoctor.convert('Simple paragraph with an inline image image:cat.png[]', options)
2826+
expect(result).to.equal('<p class="paragraph-nunjucks">Simple paragraph with an inline image <span class="image"><img src="cat.png" alt="cat"></span></p>')
2827+
})
2828+
it('should configure Handlebars environment using the template_engine_options', () => {
2829+
const options = {
2830+
safe: 'safe',
2831+
backend: 'html5',
2832+
template_dir: 'spec/fixtures/templates/handlebars',
2833+
template_cache: false, // disable template cache to recreate templates with the given options
2834+
template_engine_options: {
2835+
handlebars: {
2836+
noEscape: true
2837+
}
2838+
}
2839+
}
2840+
const result = asciidoctor.convert('Simple paragraph with an inline image image:cat.png[]', options)
2841+
expect(result).to.equal('<p class="paragraph-handlebars">Simple paragraph with an inline image <span class="image"><img src="cat.png" alt="cat"></span></p>')
2842+
})
2843+
it('should configure Pug templates using the template_engine_options', () => {
2844+
const options = {
2845+
safe: 'safe',
2846+
backend: 'html5',
2847+
template_dir: 'spec/fixtures/templates/pug',
2848+
template_cache: false, // disable template cache to recreate templates with the given options
2849+
template_engine_options: {
2850+
pug: {
2851+
doctype: 'xml'
2852+
}
2853+
}
2854+
}
2855+
const result = asciidoctor.convert('image:cat.png[]', options)
2856+
expect(result).to.equal('<p class="paragraph-pug"><img src="cat.png"></img></p>')
2857+
})
2858+
it('should configure EJS templates using the template_engine_options', () => {
2859+
const options = {
2860+
safe: 'safe',
2861+
backend: 'html5',
2862+
template_dir: 'spec/fixtures/templates/ejs-custom-delimiters',
2863+
template_cache: false, // disable template cache to recreate templates with the given options
2864+
template_engine_options: {
2865+
ejs: {
2866+
delimiter: '?',
2867+
openDelimiter: '[',
2868+
closeDelimiter: ']'
2869+
}
2870+
}
2871+
}
2872+
const result = asciidoctor.convert('A simple paragraph.', options)
2873+
expect(result).to.equal('<p class="paragraph-ejs">A simple paragraph.</p>')
2874+
})
28062875
it('should resolve conflicts consistently when the same template exists in multiple directories', () => {
28072876
// the template paragraph.njk is present in both the "nunjucks" and "nunjucks-ctx-b" directory!
28082877
// the rule is that the last one wins so the template order in "template_dirs" is important.

0 commit comments

Comments
 (0)