Packages
fermo
0.19.0
0.20.1
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.1
0.17.0
0.16.7
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.0
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.4
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A static site generator
Current section
Files
Jump to
Current section
Files
lib/fermo/compilers/eex.ex
defmodule Fermo.Compilers.EEx do
import Mix.Fermo.Paths
import Fermo.Naming
defstruct [
:name,
:source,
:frontmatter,
:content_fors,
:template_project_path,
:template_source_path,
:offset
]
@file_impl Application.compile_env(:fermo, :file_impl, File)
def compile(template_project_path) do
template_source_path = absolute_to_source(template_project_path)
name = source_path_to_module(template_source_path)
source = File.read!(template_project_path)
[frontmatter, body] = extract_frontmatter(source)
compile_module(
%__MODULE__{
name: name,
source: body,
frontmatter: frontmatter,
content_fors: [],
template_project_path: template_project_path,
template_source_path: template_source_path,
offset: 0
}
)
end
def extract_frontmatter(source = "---\n" <> _rest) do
[_, frontmatter_yaml, body] = String.split(source, "---\n")
frontmatter = YamlElixir.read_from_string(frontmatter_yaml)
[frontmatter, body]
end
def extract_frontmatter(body) do
[%{}, body]
end
def compile_module(%__MODULE__{} = module) do
# We do a first compilation here so we can trap errors
# and give a better message
try do
EEx.compile_string(module.source, line: module.offset, file: module.template_project_path)
rescue
e in TokenMissingError ->
message = """
Template compilation error: #{e.description}
Path: '#{module.template_project_path}'
"""
raise Fermo.Error, message: message
end
quoted_module = quote(
bind_quoted: [
content_fors: module.content_fors,
frontmatter: Macro.escape(module.frontmatter, unquote: true),
name: module.name,
offset: module.offset,
source: module.source,
template_project_path: module.template_project_path,
template_source_path: module.template_source_path
],
file: module.template_project_path
) do
compiled = EEx.compile_string(source, line: offset, file: template_project_path)
cfs_compiled = Enum.map(content_fors, fn [key, eex, offset] ->
cf_compiled = EEx.compile_string(eex, line: offset, file: template_project_path)
{key, cf_compiled}
end)
defmodule :"#{name}" do
use Helpers
require Fermo.Partial
import Fermo.Partial
require Fermo.YieldContent
import Fermo.YieldContent
require Fermo.Assets
import Fermo.Assets
import Fermo.I18n
Enum.map(cfs_compiled, fn {key, cf_compiled} ->
args = [:"#{key}", Macro.var(:params, nil), Macro.var(:context, nil)]
def content_for(unquote_splicing(args)) do
_params = var!(params)
_context = var!(context)
unquote(cf_compiled)
end
end)
# TODO: Shouldn't this return nil?
# TODO: if we change this to return nil,
# we need to update set_paths/1
def content_for(key, params, context) do
""
end
def template_source_path() do
unquote(template_source_path)
end
# Define a method with the frontmatter, so we can merge with
# params when the template is evaluated
escaped_frontmatter = Macro.escape(frontmatter)
def defaults() do
unquote(escaped_frontmatter)
end
args = [Macro.var(:params, nil), Macro.var(:context, nil)]
def call(unquote_splicing(args)) do
_params = var!(params)
_context = var!(context)
unquote(compiled)
end
end
end
Code.compiler_options(ignore_module_conflict: true)
[{module_name, bytecode} | _other] = Code.compile_quoted(quoted_module)
Code.compiler_options(ignore_module_conflict: false)
base = Mix.Project.compile_path()
module_path = Path.join(base, "#{module_name}.beam")
@file_impl.write!(module_path, bytecode, [:write])
end
end