Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A Scriban module for Wyam
using Scriban;
using Scriban.Parsing;
using Scriban.Runtime;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wyam.Common.Configuration;
using Wyam.Common.Documents;
using Wyam.Common.Execution;
using Wyam.Common.Modules;
namespace Wyam.Scriban
{
/*
Usage:
new ScribanModule("my_template.tpl", d => { return d; })
.WithData("name", "Deane")
.WithFunction("rapperize", new Func<string, string>(s => s + ", yo."))
.WithType("functions", typeofOf(MyTypeWithABunchOfStaticMethods))
.WithDocKey("what_my_doc_variable_should_be_called") // defaults to "doc"
.WithSettingsKey("what_the_engine_settings_should_be_called") // defaults to "settings"
.WithLayout("a_template_to_add_to_the_end.tpl")
.WithScriptObject(myCustomScriptObject)
*/
public class RenderScriban : IModule
{
private string _template;
private string _templateFile;
private string _preTemplate;
private string _preTemplateFile;
private string _postTemplate;
private string _postTemplateFile;
private DocumentConfig _modelFactory;
private string _layout;
private string _settingsKey = "settings";
private string _docKey = "doc";
private Dictionary<string, object> _data = new Dictionary<string, object>();
private Dictionary<string, Delegate> _functions = new Dictionary<string, Delegate>();
private List<Type> _types = new List<Type>();
private List<ScriptObject> _scriptObjects = new List<ScriptObject>();
public RenderScriban(DocumentConfig modelFactory)
{
_modelFactory = modelFactory;
}
public RenderScriban WithTemplate(string template)
{
_template = template;
return this;
}
public RenderScriban WithTemplateFile(string templateFile)
{
_templateFile = templateFile;
return this;
}
public RenderScriban WithPreTemplate(string preTemplate)
{
_preTemplate = preTemplate;
return this;
}
public RenderScriban WithPreTemplateFile(string preTemplateFile)
{
_preTemplateFile = preTemplateFile;
return this;
}
public RenderScriban WithPostTemplate(string postTemplate)
{
_postTemplate = postTemplate;
return this;
}
public RenderScriban WithPostTemplateFile(string postTemplateFile)
{
_postTemplateFile = postTemplateFile;
return this;
}
public RenderScriban WithData(string key, object value)
{
_data.Add(key, value);
return this;
}
public RenderScriban WithFunction(string key, Delegate value)
{
_functions.Add(key, value);
return this;
}
public RenderScriban WithLayout(string layout)
{
_layout = layout;
return this;
}
public RenderScriban WithType(Type type)
{
_types.Add(type);
return this;
}
public RenderScriban WithSettingsKey(string key)
{
_settingsKey = key;
return this;
}
public RenderScriban WithDocKey(string key)
{
_docKey = key;
return this;
}
public RenderScriban WithScriptObject(ScriptObject obj)
{
_scriptObjects.Add(obj);
return this;
}
public IEnumerable<IDocument> Execute(IReadOnlyList<IDocument> inputs, IExecutionContext context)
{
var templateString = new StringBuilder();
if (_preTemplate != null)
{
templateString.Append(_preTemplate);
}
if (_preTemplateFile != null)
{
templateString.Append(context.FileSystem.GetInputFile(_preTemplateFile).ReadAllText());
}
if(_template != null)
{
templateString.Append(_template);
}
if(_templateFile != null)
{
templateString.Append(context.FileSystem.GetInputFile(_templateFile).ReadAllText());
}
if (_postTemplate != null)
{
templateString.Append(_postTemplate);
}
if (_postTemplateFile != null)
{
templateString.Append(context.FileSystem.GetInputFile(_postTemplateFile).ReadAllText());
}
var template = Template.Parse(templateString.ToString());
// This cannot be parallel because Scriban TemplateContext is NOT thread-safe
return inputs.AsParallel().Select(context, input =>
{
// Load the main doc and the global settings
var templateVars = new ScriptObject
{
{ _docKey, _modelFactory(input, context) },
{ _settingsKey, context.Settings }
};
// Load the data that was passed in
foreach (var pair in _data)
{
templateVars.Add(pair.Key, pair.Value);
}
// Load the custom functions
foreach (var pair in _functions)
{
templateVars.Import(pair.Key, pair.Value);
}
// Load the types
foreach (var type in _types)
{
templateVars.Import(type);
}
// Load a context, with a template loader that will work off the Wyam file system
var templateContext = new TemplateContext
{
TemplateLoader = new TemplateLoader(context)
};
templateContext.PushGlobal(templateVars);
// Load the script objects
foreach(var obj in _scriptObjects)
{
templateContext.PushGlobal(obj);
}
// Execute
var result = template.Render(templateContext);
// Return a new doc
var x = context.GetDocument(input, GenerateStreamFromString(result));
return x;
});
}
public static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
public class TemplateLoader : ITemplateLoader
{
private IExecutionContext _wyamContext;
public TemplateLoader(IExecutionContext wyamContext)
{
_wyamContext = wyamContext;
}
public string GetPath(TemplateContext context, Scriban.Parsing.SourceSpan callerSpan, string templateName)
{
return _wyamContext.FileSystem.GetInputFile(templateName).Path.FullPath;
}
public string Load(TemplateContext context, Scriban.Parsing.SourceSpan callerSpan, string templatePath)
{
return _wyamContext.FileSystem.GetFile(templatePath).ReadAllText();
}
public ValueTask<string> LoadAsync(TemplateContext context, Scriban.Parsing.SourceSpan callerSpan, string templatePath)
{
throw new NotImplementedException();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment