Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@

{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80},
{Credo.Check.Readability.MaxLineLength,
priority: :low, max_length: 80, ignore_specs: true
},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
Expand All @@ -70,7 +72,7 @@
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.Specs},
{Credo.Check.Readability.Specs, false},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ cover
/priv/build
/tmp
.esm-cache
.elixir_ls
12 changes: 8 additions & 4 deletions lib/elixir_script/beam.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule ElixirScript.Beam do
For protocols, this will return a list of
all the protocol implementations
"""
@spec debug_info(atom | bitstring) :: {:ok | :error, map | binary}
@spec debug_info(atom | bitstring) :: {:ok, map} | {:ok, atom, map, list} | {:error, binary}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long (max is 80, was 94).

def debug_info(module)

# We get debug info from String and then replace
Expand Down Expand Up @@ -59,7 +59,7 @@ defmodule ElixirScript.Beam do
{:ok, {^module, attribute_info}} = :beam_lib.chunks(beam, [:attributes]) do

if Keyword.get(attribute_info[:attributes], :protocol) do
get_protocol_implementations(module)
get_protocol_implementations(module, beam_path)
else
backend.debug_info(:elixir_v1, module, data, [])
|> process_debug_info(beam_path)
Expand Down Expand Up @@ -89,14 +89,18 @@ defmodule ElixirScript.Beam do
Map.put(info, :last_modified, nil)
end

info = Map.put(info, :beam_path, beam_path)

{:ok, info}
end

defp process_debug_info(error, _) do
error
end

defp get_protocol_implementations(module) do
defp get_protocol_implementations(module, beam_path) do
{:ok, protocol_module_info} = process_debug_info({:ok, %{}}, beam_path)

implementations = module
|> Protocol.extract_impls(:code.get_path())
|> Enum.map(fn(x) -> Module.concat([module, x]) end)
Expand All @@ -109,7 +113,7 @@ defmodule ElixirScript.Beam do
end
end)

{:ok, module, implementations}
{:ok, module, protocol_module_info, implementations}
end

defp replace_definitions(original_definitions, replacement_definitions) do
Expand Down
27 changes: 24 additions & 3 deletions lib/elixir_script/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ defmodule ElixirScript.Compiler do
Translate,
FindUsedModules,
FindUsedFunctions,
Output
Output,
}
alias ElixirScript.ModuleSystems.ES
alias Kernel.ParallelCompiler

@spec compile(atom | [atom] | binary, []) :: nil
@spec compile(atom | [atom] | binary, []) :: map
def compile(path, opts \\ [])

def compile(path, opts) when is_binary(path) do
Expand Down Expand Up @@ -86,7 +86,7 @@ defmodule ElixirScript.Compiler do

State.stop(pid)

result
transform_output(modules, result, opts)
end

defp build_compiler_options(opts) do
Expand All @@ -103,4 +103,25 @@ defmodule ElixirScript.Compiler do
defp on_module_compile(pid, _file, module, beam) do
State.put_in_memory_module(pid, module, beam)
end

defp transform_output(modules, compiled_js, opts) do
output_path = if opts.output == nil or opts.output == :stdout do
""
else
Path.dirname(opts.output)
end

Enum.reduce(modules, %{}, fn {module, info}, current_data ->
info = %{
references: info.used_modules,
last_modified: info.last_modified,
beam_path: Map.get(info, :beam_path),
source: Map.get(info, :file),
js_path: Path.join(output_path, "#{module}.js"),
js_code: Keyword.get(compiled_js, module)
}

Map.put(current_data, module, info)
end)
end
end
21 changes: 21 additions & 0 deletions lib/elixir_script/manifest.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule ElixirScript.Manifest do
@moduledoc false

@spec read_manifest(binary) :: nil
def read_manifest(_manifest) do

end

@spec write_manifest(binary, map) :: :ok
def write_manifest(manifest_path, modules) do
data = Enum.reduce(modules, %{}, fn {module, info}, current_data ->
Map.put(current_data, module, Map.drop(info, [:js_code]))
end)

data = :erlang.term_to_binary(data, [:compressed])
File.mkdir_p!(Path.dirname(manifest_path))
File.write!(manifest_path, data)

:ok
end
end
32 changes: 18 additions & 14 deletions lib/elixir_script/passes/find_used_modules.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule ElixirScript.FindUsedModules do
@doc """
Takes a list of entry modules and finds modules they use.
"""
@spec execute([atom], pid) :: nil
@spec execute([atom], pid) :: :ok
def execute(modules, pid) do
modules
|> List.wrap
Expand All @@ -26,8 +26,8 @@ defmodule ElixirScript.FindUsedModules do
case result do
{:ok, info} ->
walk_module(module, info, pid)
{:ok, module, implementations} ->
walk_protocol(module, implementations, pid)
{:ok, module, module_info, implementations} ->
walk_protocol(module, module_info, implementations, pid)
{:error, "Unknown module"} ->
Logger.warn fn() ->
"ElixirScript: #{inspect module} is missing or unavailable"
Expand Down Expand Up @@ -83,7 +83,7 @@ defmodule ElixirScript.FindUsedModules do
end)
end

defp walk_protocol(module, implementations, pid) do
defp walk_protocol(module, module_info, implementations, pid) do
impls = Enum.map(implementations, fn {impl, %{attributes: attrs}} ->
protocol_impl = Keyword.fetch!(attrs, :protocol_impl)
impl_for = Keyword.fetch!(protocol_impl, :for)
Expand All @@ -94,7 +94,12 @@ defmodule ElixirScript.FindUsedModules do

functions = Enum.map(first_implementation_functions, fn { name, _, _, _} -> name end)

ModuleState.put_module(pid, module, %{protocol: true, impls: impls, functions: functions})
module_info = Map.merge(
module_info,
%{protocol: true, impls: impls, functions: functions}
)

ModuleState.put_module(pid, module, module_info)

Enum.each(implementations, fn {impl, info} ->
ModuleState.add_used_module(pid, module, impl)
Expand Down Expand Up @@ -303,15 +308,14 @@ defmodule ElixirScript.FindUsedModules do
end

defp walk({:., _, [module, function]}, state) do
cond do
ElixirScript.Translate.Module.is_elixir_module(module) ->
ModuleState.add_used_module(state.pid, state.module, module)
if ModuleState.get_module(state.pid, module) == nil do
do_execute(module, state.pid)
end
true ->
walk(module, state)
walk(function, state)
if ElixirScript.Translate.Module.is_elixir_module(module) do
ModuleState.add_used_module(state.pid, state.module, module)
if ModuleState.get_module(state.pid, module) == nil do
do_execute(module, state.pid)
end
else
walk(module, state)
walk(function, state)
end
end

Expand Down
29 changes: 8 additions & 21 deletions lib/elixir_script/passes/output.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ defmodule ElixirScript.Output do
@doc """
Takes outputs the JavaScript code in the specified output
"""
@spec execute([atom], pid, map) :: nil
@spec execute([atom], pid, map) :: [{atom, binary}]
def execute(modules, pid, opts) do
modules = modules
prepared_modules = modules
|> Enum.filter(fn {_, info} -> Map.has_key?(info, :js_ast) end)
|> Enum.map(fn {module, info} ->
{module, info.js_ast, info.used_modules}
Expand All @@ -27,8 +27,7 @@ defmodule ElixirScript.Output do
{module, name, path, import_path}
end)

modules
|> create_modules(opts, js_modules)
create_modules(prepared_modules, opts, js_modules)
end

defp concat(code) do
Expand All @@ -39,17 +38,6 @@ defmodule ElixirScript.Output do
"""
end

defp prepare_js_ast(js_ast) do
case js_ast do
modules when is_list(modules) ->
modules
|> Enum.reduce([], &(&2 ++ &1.body))
|> Builder.program
_ ->
js_ast
end
end

defp create_modules(modules, opts, js_modules) do
modules
|> Task.async_stream(fn({module, [body, exports], used_modules}) ->
Expand All @@ -62,7 +50,6 @@ defmodule ElixirScript.Output do

js_parts
|> Builder.program
|> prepare_js_ast
|> Generator.generate
|> concat
|> output(module, Map.get(opts, :output), js_modules)
Expand All @@ -84,12 +71,10 @@ defmodule ElixirScript.Output do
|> String.replace(".", "$")
end

defp output(code, _, nil, _) do
code
end

defp output(code, _, :stdout, _) do
defp output(code, module, nil, _), do: {module, code}
defp output(code, module, :stdout, _) do
IO.puts(code)
{module, code}
end

defp output(code, module, path, js_modules) do
Expand All @@ -112,6 +97,8 @@ defmodule ElixirScript.Output do

copy_bootstrap_js(output_dir)
File.write!(file_name, code)

{module, code}
end

defp copy_bootstrap_js(directory) do
Expand Down
10 changes: 9 additions & 1 deletion lib/mix/tasks/compile.elixir_script.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
defmodule Mix.Tasks.Compile.ElixirScript do
use Mix.Task
alias ElixirScript.Manifest

@recursive true
@manifest ".compile.elixir_script"
@manifest_vsn 1

@moduledoc """
Mix compiler to allow mix to compile Elixirscript source files into JavaScript
Expand Down Expand Up @@ -41,13 +44,18 @@ defmodule Mix.Tasks.Compile.ElixirScript do

defp do_compile() do
{input, opts} = get_compiler_params()
ElixirScript.Compiler.compile(input, opts)
result = ElixirScript.Compiler.compile(input, opts)

Manifest.write_manifest(manifest(), result)
end

def clean do
:ok
end

def manifests, do: [manifest()]
defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest)

@doc false
def get_compiler_params() do
elixirscript_config = get_elixirscript_config()
Expand Down
2 changes: 1 addition & 1 deletion test/beam_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ defmodule ElixirScript.Beam.Test do
end

test "can get ast from beam that is protocol" do
assert {:ok, Enumerable, _} = ElixirScript.Beam.debug_info(Enumerable)
assert {:ok, Enumerable, _, _} = ElixirScript.Beam.debug_info(Enumerable)
end
end
6 changes: 3 additions & 3 deletions test/compiler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ defmodule ElixirScript.Compiler.Test do

test "Can compile one entry module" do
result = ElixirScript.Compiler.compile(Version)
assert is_binary(hd(result))
assert result |> Map.to_list |> hd |> elem(1) |> Map.get(:js_code) |> is_binary
end

test "Can compile multiple entry modules" do
result = ElixirScript.Compiler.compile([Atom, String, Agent])
assert is_binary(hd(result))
assert result |> Map.to_list |> hd |> elem(1) |> Map.get(:js_code) |> is_binary
end

test "Output" do
result = ElixirScript.Compiler.compile(Atom, [])
assert hd(result) =~ "export default"
assert result |> Map.to_list |> hd |> elem(1) |> Map.get(:js_code) =~ "export default"
end

test "compile file" do
Expand Down
12 changes: 12 additions & 0 deletions test/manifest_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule ElixirScript.Manifest.Test do
use ExUnit.Case
alias ElixirScript.Manifest

test "write manifest" do
result = ElixirScript.Compiler.compile(Atom)
path = Path.join([System.tmp_dir(), "write_manifest_test", ".compile.elixir_script"])
Manifest.write_manifest(path, result)
assert File.exists?(path)
end

end