     |> do_call_function(fun, args, arg)
   end
 
-  @spec do_call_function([t()], atom(), [term()], term()) :: Result.t(term())
-  defp do_call_function(plugins, fun, args, acc)
+  @spec do_call_function([{integer, t()}], atom(), [term()], term()) :: Result.t(term())
+  defp do_call_function(arity_and_plugins, fun, args, acc)
   defp do_call_function([], _fun, _args, acc), do: {:ok, acc}
 
-  defp do_call_function([plugin | plugins], fun, args, acc) do
-    case apply(plugin.module, fun, [acc | args]) do
+  defp do_call_function([{arity, plugin} | arity_and_plugins], fun, args, acc) do
+    new_args = update_callback_args(args, plugin, fun, arity)
+
+    case apply(plugin.module, fun, [acc | new_args]) do
       {:ok, new_acc} ->
-        do_call_function(plugins, fun, args, new_acc)
+        do_call_function(arity_and_plugins, fun, args, new_acc)
 
       {:error, _} = error ->
         error

    end
  rescue
    exception -> handle_exception(exception, plugin.module, fun)
  end

  @spec handle_exception(Exception.t(), atom(), atom()) :: Result.t()

  @type spec :: atom() | {atom(), plugin_options()}
  @type plugin_options :: [only: atom() | [atom()], args: term()]

  @old_callback_arities [
    build_started: 2,
    reading_pages: 1,
    reading_posts: 1,
    reading_templates: 1,
    processing_page: 1,
    processing_post: 1,
    processing_template: 1,
    processed_page: 1,
    processed_post: 1,
    processed_template: 1,
    processed_list: 1,
    processed_pages: 1,
    processed_posts: 1,
    rendering_fragment: 2,
    rendered_fragment: 1,
    rendered_page: 1,
    wrote_file: 1,
    build_succeeded: 2,
    build_failed: 3,
    finalizing: 2
  ]

  @optional_callbacks @old_callback_arities
                      |> Enum.map(fn {name, arity} ->
                        [{name, arity}, {name, arity + 1}]
                      end)
