Current section
Files
Jump to
Current section
Files
lib/mix/tasks/convert.ex
defmodule Mix.Tasks.Dragon.Convert do
@moduledoc false
@shortdoc "Convert a Jekyll Project to a Dragon Project"
use Mix.Task
import Mix.Generator
use Dragon.Context
import Dragon.Tools.File.WalkTree
import Dragon.Tools.File
@templates Mix.Tasks.Dragon.New
@impl true
def run([target]), do: convert(target)
def run(_), do: IO.puts("Syntax: mix dragon.convert {target}")
def is_symlink?(path) do
case File.lstat(path) do
{:ok, %{type: :symlink}} -> true
_ -> false
end
end
defp safe_mvdir(src, dst) do
if is_symlink?(src) do
warn("not moving #{src} because it is a symlink")
else
if File.dir?(src) do
if File.exists?(dst) do
warn("Cannot move #{src} to #{dst}: already exists")
else
stdout([:green, "* moving #{src} to #{dst}"])
:ok = File.rename(src, dst)
end
else
create_directory(dst)
end
end
end
def convert(target) do
p = fn x -> Path.join(target, x) end
stdout([:green, "* cleaning old files"])
# remove old unecessary files
["Gemfile", "Gemfile.lock", "_site", ".jekyll-cache"]
|> Enum.each(fn f ->
IO.puts("Removing #{f}")
File.rm_rf!(p.(f))
end)
create_file(p.("_dragon.yml"), @templates.dragon_config([]))
Application.ensure_all_started(:dragon)
{:ok, dragon} = Dragon.get()
safe_mvdir(p.("_includes"), p.("_lib"))
safe_mvdir(p.("_layouts"), p.(Path.join("_lib", "_layout")))
create_directory(p.("_data"))
jekyll_cfg = p.("_config.yml")
target_site = p.(Path.join("_data", "_site.yml"))
if File.exists?(jekyll_cfg) do
if File.exists?(target_site) do
warn("Cannot move #{jekyll_cfg} to #{target_site}: already exists")
else
:ok = File.rename(p.("_config.yml"), p.(Path.join("_data", "_site.yml")))
end
end
walk_tree(dragon, target, follow_meta: true, no_match: &handle_file/3)
IO.puts(
"\nConversion complete. This is only a blind-conversion on some known parts; you will still have to do more work."
)
end
defp has_separator?(path),
do: with_open_file(path, fn fd -> IO.binread(fd, 3) end) == "---"
defp convert_line("---\n", {:start, []}), do: {:head, ["--- dragon-1.0\n"]}
defp convert_line("---\n", {:head, buf}), do: {:body, ["--- eex\n" | buf]}
defp convert_line("layout:" <> file, {:head, buf}),
do: {:head, ["@spec:\n layout: #{String.trim(file)}.html\n" | buf]}
defp convert_line(line, {where, buf}), do: {where, [convert_liquid(line) | buf]}
defp convert_liquid(line) do
line
|> String.replace(~r/\s*-?[%}]}/, "%}")
|> String.replace(~r/{[%{]-?\s*/, "{%")
|> match_expression()
end
def match_expression(line),
do: Regex.replace(~r/{%(.+?(?=%}))%}/u, line, &convert_expression/2, global: true)
def convert_expression(_, expr) do
fixed =
case String.split(expr, " ") do
["if" | rest] ->
"= if #{Enum.join(rest, " ")} do"
|> String.replace(~r/^= if (page|site)/, "= if @\\1")
["else", "if" | rest] ->
warn("WARNING: else if conversion does not add an extra 'end' in the template")
"= else if #{Enum.join(rest, " ")} do"
# this isn't even right but it's out there...
["elsif" | rest] ->
warn("WARNING: else if conversion does not add an extra 'end' in the template")
"= else if #{Enum.join(rest, " ")} do"
["assign" | rest] ->
" " <> Enum.join(rest, " ")
["for" | rest] ->
body =
Enum.map(rest, fn
"in" -> "<-"
x -> x
end)
"= for #{Enum.join(body, " ")} do"
["include", file | rest] ->
args =
rest
|> Enum.filter(&(&1 != ""))
|> Enum.map(&String.replace(&1, "=", ": "))
"= include " <> Enum.join(["\"/_lib/#{file}\""] ++ args, ", ")
["else" | _] ->
" else"
["endif" | _] ->
" end"
["endfor" | _] ->
" end"
_miss ->
"= " <> expr
# |> tap(&IO.puts(">>> MISSED: #{&1}"))
end
|> String.replace(~r/ +do$/, " do")
|> String.replace("| markdownify", "|> markdownify")
|> String.replace(~r/^= (page|site)/, "= @\\1")
"<%#{fixed} %>"
end
defp convert_file(path) do
stdout([:green, "* converting #{path}"])
{_, content} =
with_open_file(path, fn fd ->
IO.stream(fd, :line)
|> Enum.reduce({:start, []}, &convert_line/2)
end)
File.write(path, Enum.reverse(content))
end
@always_convert MapSet.new([".html", ".htm"])
defp always_convert?(ext), do: MapSet.member?(@always_convert, ext)
defp handle_file(dragon, path, opts) do
ext = Path.extname(path)
if has_separator?(path) or (opts.in_meta and always_convert?(ext)), do: convert_file(path)
dragon
end
end