local minijinja = require("minijinja")
local filter = {}
local JinjaSettings = {
reload_before_render = nil,
keep_trailing_newline = nil,
trim_blocks = nil,
lstrip_blocks = nil,
debug = nil,
fuel = nil,
recursion_limit = nil,
undefined_behavior = nil,
pycompat = nil,
context = nil,
}
local JSON_EXTS = pandoc.List({ ".json" })
local YAML_EXTS = pandoc.List({ ".yaml", ".yml" })
local function read_file(path)
local file = io.open(path, "r")
if not file then
pandoc.log.error("failed to read file: " .. path)
return
end
local content = file:read("a")
file:close()
return content
end
local function normalize_metadata(meta)
local ctx = {}
for k, v in pairs(meta) do
local t = pandoc.utils.type(v)
if t == "Inlines" or t == "Blocks" then
if t == "Inlines" then
v = pandoc.Blocks(v)
end
v = pandoc.write(pandoc.Pandoc(v), "markdown"):gsub("\n$", "")
elseif t == "table" then
v = normalize_metadata(v)
end
ctx[k] = v
end
return ctx
end
local function has_json_ext(ext)
return JSON_EXTS:includes(ext)
end
local function has_yaml_ext(ext)
return YAML_EXTS:includes(ext)
end
local function load_json(path)
local json = read_file(path)
if json == nil then return end
local ctx = pandoc.json.decode(json, false)
if pandoc.utils.type(ctx) ~= "table" then
pandoc.error("invalid json: ", pandoc.utils.stringify(ctx))
return
end
return ctx
end
local function load_yaml(path)
local yaml = read_file(path)
if yaml == nil then return end
local ctx = pandoc.read(yaml, "markdown").meta
if pandoc.utils.type(ctx) ~= "table" then
pandoc.error("invalid yaml: ", pandoc.utils.stringify(ctx))
return
end
return ctx
end
local function load_context_from_file(path)
if not pandoc.path.exists(path) then
pandoc.log.error("file does not exist: " .. path)
return
end
local _, ext = pandoc.path.split_extension(path)
local is_json = has_json_ext(ext)
local is_yaml = has_yaml_ext(ext)
if not (is_json or is_yaml) then
pandoc.log.error("only JSON and YAML files are supported: " .. path)
end
if is_json then
return load_json(path)
end
if is_yaml then
return load_yaml(path)
end
end
local function load_context(context)
local is_string = pandoc.utils.type(context) == "string"
local is_table = pandoc.utils.type(context) == "table"
if not (is_string or is_table) then
pandoc.log.error("`context` must be a filepath or a table: " .. context)
return
end
local ctx
if is_table then
ctx = context
end
if is_string then
ctx = load_context_from_file(context)
end
if ctx ~= nil then
ctx = normalize_metadata(ctx)
end
return ctx
end
local function Meta(meta)
local mj = meta.minijinja
if mj == nil then return end
local context = mj.context
if context ~= nil then
JinjaSettings.context = load_context(context)
end
JinjaSettings.reload_before_render = mj.reload_before_render
JinjaSettings.keep_trailing_newline = mj.keep_trailing_newline
JinjaSettings.trim_blocks = mj.trim_blocks
JinjaSettings.lstrip_blocks = mj.lstrip_blocks
JinjaSettings.debug = mj.debug
JinjaSettings.fuel = mj.fuel
JinjaSettings.recursion_limit = mj.recursion_limit
JinjaSettings.undefined_behavior = mj.undefined_behavior
JinjaSettings.pycompat = mj.pycompat
end
function filter.Pandoc(doc)
doc = doc:walk({ Meta = Meta })
local env = minijinja.Environment:new()
if JinjaSettings.reload_before_render ~= nil then
env.reload_before_render = JinjaSettings.reload_before_render
end
if JinjaSettings.keep_trailing_newline ~= nil then
env.keep_trailing_newline = JinjaSettings.keep_trailing_newline
end
if JinjaSettings.trim_blocks ~= nil then
env.trim_blocks = JinjaSettings.trim_blocks
end
if JinjaSettings.lstrip_blocks ~= nil then
env.lstrip_blocks = JinjaSettings.lstrip_blocks
end
if JinjaSettings.debug ~= nil then
env.debug = JinjaSettings.debug
end
if JinjaSettings.fuel ~= nil then
env.fuel = JinjaSettings.fuel
end
if JinjaSettings.recursion_limit ~= nil then
env.recursion_limit = JinjaSettings.recursion_limit
end
if JinjaSettings.undefined_behavior ~= nil then
env.undefined_behavior = JinjaSettings.undefined_behavior
end
env:set_pycompat(JinjaSettings.pycompat)
local source = pandoc.write(doc, "markdown")
local rendered = env:render_str(source, JinjaSettings.context, PANDOC_STATE.output_file)
return pandoc.read(rendered, "markdown")
end
return filter