   @doc """
   Requires the given files.
 
-  A callback that is invoked every time a file is required
-  can be optionally given as argument.
+  A callback that will be invoked with each file, or a keyword list of `callbacks` can be provided:
+
+    * `:each_file` - invoked with each file
+
+    * `:each_module` - invoked with file, module name, and binary code
 
   Returns the modules generated by each required file.
   """
-  def files(files, callback \\ fn x -> x end) do
+  def files(files, callbacks \\ [])
+
+  def files(files, callback) when is_function(callback, 1) do
+    files(files, [each_file: callback])
+  end
+
+  def files(files, callbacks) when is_list(callbacks) do
     compiler_pid = self()
     :elixir_code_server.cast({:reset_warnings, compiler_pid})
     schedulers = max(:erlang.system_info(:schedulers_online), 2)
-    result = spawn_requires(files, [], callback, schedulers, [])
+    result = spawn_requires(files, [], callbacks, schedulers, [])
 
     # In case --warning-as-errors is enabled and there was a warning,
     # compilation status will be set to error.


          case status do
            {:required, mods, file} ->
              callback.(file)
              spawn_requires(files, waiting, callback, schedulers, mods ++ result)
            {:failure, kind, reason, stacktrace} ->
              :erlang.raise(kind, reason, stacktrace)
            other ->
              :erlang.raise(:exit, other, [])
          end
        else
          spawn_requires(files, waiting, callback, schedulers, result)
        end

      {:module_available, child, ref, _, _, _} ->
        send(child, {ref, :ack})
        spawn_requires(files, waiting, callback, schedulers, result)
      {:struct_available, _} ->
        spawn_requires(files, waiting, callback, schedulers, result)
      {:waiting, _, child, ref, _, _} ->
        send(child, {ref, :not_found})
        spawn_requires(files, waiting, callback, schedulers, result)
    end
  end
end

    end
  end

  defp spawn_requires([], [], _callbacks, _schedulers, result), do: result

  defp spawn_requires([], waiting, callbacks, schedulers, result) do
    wait_for_messages([], waiting, callbacks, schedulers, result)
  end

  defp spawn_requires(files, waiting, callbacks, schedulers, result) when length(waiting) >= schedulers do
    wait_for_messages(files, waiting, callbacks, schedulers, result)
  end

  defp spawn_requires([h | t], waiting, callbacks, schedulers, result) do
    parent = self()
    {pid, ref} = :erlang.spawn_monitor fn ->
      :erlang.put(:elixir_compiler_pid, parent)
