liquid
Version:
Node.js port of the Liquid template engine
105 lines (88 loc) • 2.94 kB
JavaScript
const Liquid = require('../liquid')
module.exports = class Template {
constructor () {
this.registers = {}
this.assigns = {}
this.instanceAssigns = {}
this.tags = {}
this.errors = []
this.rethrowErrors = true
}
async parse (engine, source = '') {
this.engine = engine
// whitespace control
//
// In Liquid, you can include a hyphen in your tag syntax {{-, -}}, {%-, and -%}
// to strip whitespace from the left or right side of a rendered tag.
// https://shopify.github.io/liquid/basics/whitespace/
source = source
.replace(/\n.*?(\{%)-/g, '$1') // tag starting with {%-
.replace(/-(%\}).*?\n/g, '$1') // tag ending with -%}
.replace(/\n.*?(\{\{)-/g, '$1') // tag starting with {{-
.replace(/-(}\}).*?\n/g, '$1') // tag ending with -}}
const tokens = this._tokenize(source)
this.tags = this.engine.tags
this.root = new Liquid.Document(this)
await this.root.parseWithCallbacks(tokens)
return this
}
async render (assigns, options) {
if (!this.root) {
throw new Error('No document root. Did you parse the document yet?')
}
let context
if (assigns instanceof Liquid.Context) {
context = assigns
} else if (assigns instanceof Object) {
assigns = [assigns, this.assigns]
context = new Liquid.Context(this.engine, assigns, this.instanceAssigns, this.registers, this.rethrowErrors)
} else if (assigns == null) {
context = new Liquid.Context(this.engine, this.assigns, this.instanceAssigns, this.registers, this.rethrowErrors)
} else {
throw new Error('Expected Object or Liquid::Context as parameter, but was ' + (typeof assigns) + '.')
}
if (options && options.registers) {
const ref = options.registers
for (const key in ref) {
if (!Object.prototype.hasOwnProperty.call(ref, key)) continue
const value = ref[key]
this.registers[key] = value
}
}
if (options && options.filters) {
context.registerFilters.apply(context, options.filters)
}
const chunks = await this.root.render(context)
try {
const result = await Liquid.Helpers.toFlatString(chunks)
this.errors = context.errors
return result
} catch (err) {
this.errors = context.errors
throw err
}
}
_tokenize (source) {
source = String(source)
if (source.length === 0) {
return []
}
const tokens = source
.split(Liquid.TemplateParser)
.filter(token => token.length > 0)
let line = 1
let col = 1
return tokens.map(value => {
const result = { value, col, line }
const lastIndex = value.lastIndexOf('\n')
if (lastIndex < 0) {
col += value.length
} else {
const linebreaks = value.split('\n').length - 1
line += linebreaks
col = value.length - lastIndex
}
return result
})
}
}