Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Coffeescript Source Maps in Rails
# config/initializers/source_maps.rb
if Rails.env.development?
require 'open3'
module CoffeeScript
class SourceMapError < StandardError; end;
class << self
def map_dir
return @map_dir if @map_dir
# Make the directory the maps are served from
@map_dir = Rails.root.join("public/source_maps")
@map_dir.mkpath
@map_dir
end
def check_coffeescript_version
version = `coffee --version`
unless version.match(/(\d+)\.\d+\./)[1].to_i >= 2
raise "You must have coffeescript version 2.0.0-dev or higher to use source maps - see http://ryanflorence.com/2012/coffeescript-source-maps/"
end
end
def compile script, options
script = script.read if script.respond_to?(:read)
if options.key?(:no_wrap) and !options.key?(:bare)
options[:bare] = options[:no_wrap]
else
options[:bare] = false
end
check_coffeescript_version
compile_with_source_map script, options
end
def compile_with_source_map script, options
flags = %w(--js)
flags << "--bare" if options[:bare]
javascript, stderr, status = Open3.capture3("coffee #{flags.join(' ')}", stdin_data: script)
raise SourceMapError, stderr unless status.success?
source_map_comment = generate_source_map options[:pathname], script if options[:pathname]
return javascript << source_map_comment
rescue SourceMapError => e
message = e.message.lines.first.chomp.gsub('"', '\"')
relative_path_name = options[:pathname].to_s.gsub(Rails.root.to_s, '') if options[:pathname]
relative_path_name ||= '<unknown path>'
%Q{throw Error("Coffeescript compile error: #{relative_path_name}: #{message}")}
end
def generate_source_map pathname, script
basename = pathname.basename('.coffee')
coffee_file = map_dir.join("#{basename}.coffee")
coffee_file.open('w') {|f| f.puts script }
# This command actually generates the source map, and saves it to a file
source_map, status = nil
Dir.chdir Rails.root.join('public/source_maps').to_s do
source_map, stderr, status = Open3.capture3("coffee --source-map -i #{pathname.basename}")
end
raise SourceMapError, "Error while generating source map for file #{pathname}: #{stderr}" unless status.success?
# I couldn't figure out how to control the 'file' and 'sources' values in the output,
# so parse the map to JSON and rewrite these to ones that will work here
data = JSON.parse(source_map)
data['file'] = pathname.basename.to_s.gsub(/\.coffee/, '')
data['sources'] = [pathname.basename.to_s]
map_file = map_dir.join "#{basename}.map"
map_file.open('w') { |f| f.puts data.to_json }
Rails.logger.info "Compiled source map #{map_file}"
return "\n//@ sourceMappingURL=/source_maps/#{map_file.basename}\n"
end
end
end
# Monkeypatch this method to include the scripts' pathname
require 'tilt/template'
module Tilt
class CoffeeScriptTemplate < Template
def evaluate(scope, locals, &block)
@output ||= CoffeeScript.compile(data, options.merge(pathname: scope.pathname))
end
end
end
end

I found that I needed two newlines on line 85 because something in the pipeline was adding a semicolon to the end of the file, which meant that the browser wouldn't find the source map.

Also, I was disappointed to find that you can't actually edit the CoffeeScript in the DevTools :( Any idea whether this is/will be possible?

almog commented Nov 2, 2012

@SimonWade Editing coffee code inside the DevTools debugging isn't possible.
Chrome doesn't know how to run coffeescript, but thanks to CoffeeScriptRedux compiler SMAP feature, it does have a 1 to 1 mapping between the compiled js code to the original coffee code.

sdbondi commented Jan 19, 2013

Could someone please explain how to use this script, or how to get source maps to generate via the pipeline?
I have put source_maps.rb in config/initializers, but not sure what else is required.
I'm new to rails so bear with me ;)

EDIT: Through a bit of searching/looking at the link in the code: I was missing CoffeeScriptRedux - DOH - (a.k.a CoffeeScript 2) - just need to make sure that your dev webserver is using the correct 'coffee' binary (v2.0.0-x). Make sure you restart your server as any files in the /initializers directory won't be 'refreshed'. Also delete your /tmp to make sure your scripts are refreshed and the source map directive is added.

sdbondi commented Jan 19, 2013

@SimonWade: I ran into the same problem but can't get rid of the semi-colon. Any ideas? I've tried adding newlines

@sdbondi did you menage to solve problem with semi-colon? I`ve got the same.

Owner

alexspeller commented Feb 24, 2013

Hi, not sure exactly what's adding semi-colons in the pipeline this is still working for me as-is on rails version 3.2.12.

Full instructions are available in my blog post about this. @michaltaberski @sdbondi please could you paste an example of actual output with the extra semicolon?

turadg commented Mar 4, 2013

Anyone working on compiling in-process now that the coffee-script-source gem 1.6.1 and later support source maps?

obie commented Mar 26, 2013

If this isn't working for you because the generated source maps are missing their filename then try my workaround here: https://gist.github.com/obie/5247309

Issue for underlying problem filed with coffescript project jashkenas/coffee-script#2806

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment