       end
     end)
     IO.puts "Generated essential templates into `#{dir}templates/`."
-    dir
+    {:ok, dir}
   end
 
-  defp init(dir, :gitignore) do
+  @docp """
+  Generates the initial `.gitignore` file.
+  """
+  @spec init_gitignore(ok_result) :: ok_result
+  defp init_gitignore({:ok, dir}) do
     File.open!("#{dir}.gitignore", [:write, :utf8], fn f ->
       IO.write(f, "site\n")
     end)
     IO.puts "Generated `#{dir}.gitignore`."
-    dir
+    {:ok, dir}
   end
 end

      IO.write(f, projinfo)
    end)
    IO.puts "Generated `#{dir}serum.json`."
    dir
  end

  defp init(dir, :page) do
    File.open!("#{dir}pages/index.md", [:write, :utf8], fn f ->
      IO.write(f, "*Hello, world!*\n")
    end)

  **NOTE:** If the directory `dir` is not empty, some contents in that
  directory may be overwritten *without a question*.
  """
  @spec init(dirname) :: :ok
  def init(dir) do
    dir = if String.ends_with?(dir, "/"), do: dir, else: dir <> "/"

    :ok =
      dir
        |> check_dir
        |> init_dir
        |> init_info
        |> init_index
        |> init_templates
        |> init_gitignore
        |> finish
  end

  @docp """
  Checks if the specified directory already exists. Prints a warning message
  if so.
  """
  @spec check_dir(dirname) :: ok_result
  defp check_dir(dir) do
    if File.exists? dir do
      IO.puts "\x1b[93mWarning: The directory `#{dir}` " <>
              "already exists and might not be empty.\x1b[0m"
    end
    {:ok, dir}
  end

  @docp """
  Prints an information message after successfully initializing a new project.
  """
  @spec finish(ok_result) :: :ok
  defp finish({:ok, dir}) do
    IO.puts "\n\x1b[1mSuccessfully initialized a new Serum project!"
    IO.puts "try `serum build #{dir}` to build the site.\x1b[0m\n"
  end

  @docp """
  Creates necessary directory structure under the specified directory.
  """
  @spec init_dir(ok_result) :: ok_result
  defp init_dir({:ok, dir}) do
    ["posts", "pages", "media", "templates",
     "assets/css", "assets/js", "assets/images"]
    |> Enum.each(fn x ->
      File.mkdir_p!("#{dir}#{x}")
      IO.puts "Created directory `#{dir}#{x}`."
    end)
    {:ok, dir}
  end

  @docp """
  Generates default project metadata files.
  """
  @spec init_info(ok_result) :: ok_result
  defp init_info({:ok, dir}) do
    projinfo =
      %{site_name: "New Website",
        site_description: "Welcome to my website!",
