Create a gist now

Instantly share code, notes, and snippets.

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
@aexmachina

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
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
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
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

@michaltaberski

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

@alexspeller
Owner

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
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
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/coffeescript#2806

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