Last active
July 3, 2022 03:57
-
-
Save rtfeldman/db7b121100b6c6ff435b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Elm Sprockets Integration for Rails 3 | |
# | |
# I'm working on turning this into a Gem - see https://github.com/NoRedInk/sprockets-elm - but | |
# I know a lot more about Elm than I do Rails, and haven't gotten that version working yet. | |
# If you know how to help Gemify this, by all means please hit me up! https://twitter.com/rtfeldman | |
# I could definitely use the help. | |
# | |
# Anyway, in the meantime, this is what we're using at NoRedInk to integrate Elm into our asset | |
# pipeline, and it works like a charm. Just copy this into config/initializers/elm_sprockets.rb | |
# | |
# For this to work, make sure you have elm-make on your PATH. You can get it with: | |
# | |
# npm install -g elm | |
# | |
# Now you should be able to //=require any *.js.elm file (must end with .js.elm and not just .elm!) | |
# from any JS file that Sprockets knows about, e.g. | |
# | |
# //= require MyElmModule.js.elm | |
require 'open3' | |
require 'tempfile' | |
class ElmCompiler < Sprockets::Processor | |
def self.default_mime_type | |
'text/x-elm' | |
end | |
def evaluate(context, _locals) | |
pathname = context.pathname.to_s | |
if pathname =~ /\.js.*\.elm$/ | |
add_elm_dependencies pathname, context | |
ElmCompiler.compile pathname | |
else | |
"" | |
end | |
end | |
private | |
# Add all Elm modules imported in the target file as dependencies, then | |
# recursively do the same for each of those dependent modules. | |
def add_elm_dependencies(filepath, context) | |
# Turn e.g. ~/NoRedInk/app/assets/javascripts/Quiz/QuestionStoreAPI.js.elm | |
# into just ~/NoRedInk/app/assets/javascripts/ | |
dirname = context.pathname.to_s.gsub Regexp.new(context.logical_path + ".+$"), "" | |
File.read(filepath).each_line do |line| | |
# e.g. `import Quiz.QuestionStore exposing (..)` | |
match = line.match(/^import\s+([^\s]+)/) | |
unless match.nil? | |
# e.g. Quiz.QuestionStore | |
module_name = match.captures[0] | |
# e.g. Quiz/QuestionStore | |
dependency_logical_name = module_name.gsub(".", "/") | |
# e.g. ~/NoRedInk/app/assets/javascripts/Quiz/QuestionStore.elm | |
dependency_filepath = dirname + dependency_logical_name + ".elm" | |
# If we don't find the dependency in our filesystem, assume it's because | |
# it comes in through a third-party package rather than our sources. | |
if File.file? dependency_filepath | |
context.depend_on dependency_logical_name | |
add_elm_dependencies dependency_filepath, context | |
end | |
end | |
end | |
end | |
def self.compile(pathname) | |
temp_file = Tempfile.new 'compiled_elm_output.js' | |
cmd = `node -e "process.stdout.write(require('elm').getPathTo('elm-make'));"` | |
begin | |
# need to specify LANG or else build will fail on jenkins | |
# with error "elm-make: elm-stuff/build-artifacts/NoRedInk/NoRedInk/1.0.0/Quiz-QuestionStore.elmo: hGetContents: invalid argument (invalid byte sequence)" | |
Open3.popen3({'LANG' => 'en_US.UTF-8'}, cmd, pathname.to_s, "--yes", "--output", temp_file.path, chdir: Rails.root) do |_stdin, stdout, stderr, wait_thr| | |
compiler_output = stdout.gets(nil) | |
stdout.close | |
compiler_err = stderr.gets(nil) | |
stderr.close | |
process_status = wait_thr.value | |
if process_status.exitstatus != 0 | |
raise compiler_err | |
end | |
end | |
temp_file.read | |
ensure | |
temp_file.unlink | |
end | |
end | |
end | |
Rails.application.assets.register_engine('.elm', ElmCompiler) | |
Rails.application.assets.register_mime_type('text/x-elm', '.elm') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment