Skip to content

Instantly share code, notes, and snippets.

@richardkmichael
Last active March 5, 2021 16:39
Show Gist options
  • Save richardkmichael/8b2ac9df7de17cc17a6abe08a1e388f7 to your computer and use it in GitHub Desktop.
Save richardkmichael/8b2ac9df7de17cc17a6abe08a1e388f7 to your computer and use it in GitHub Desktop.
Sprockets 3.6 CoffeeScript processor with sourcemaps.
load_paths:
- src
logical_paths:
- simple.js
build:
logical_paths:
- simple.js
path: dist
# For sprockets 3.6.0, as required by blade 0.7.0, as required by trix 1.3.1
# Inspired by UglifyWithSourceMaps
# https://blog.experteer.engineering/generating-sourcemaps-with-sprockets-3-and-uglify.html
# Google Doc SourceMaps Specification
# https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
# FIXME
# - How to register a different CoffeeScript processor with Sprockets? Does this exist:
# `Sprockets.register_processor(...)`
require 'sprockets/digest_utils'
require 'sprockets/autoload'
module Sprockets
# Processor engine class for the CoffeeScript compiler.
# Depends on the `coffee-script` and `coffee-script-source` gems.
#
# For more infomation see:
#
# https://github.com/josh/ruby-coffee-script
#
module CoffeeScriptProcessor
VERSION = '1'
def self.cache_key
@cache_key ||= "#{name}:#{Autoload::CoffeeScript::Source.version}:#{VERSION}".freeze
end
# Returning a string here is short-hand for `{data: <string>}`; other return hash keys are possible.
#
# Helpful input keys:
# input[:filename] # => CoffeeScript filename (could be ERB, then CoffeeScript)
# input[:name] # => CoffeeScript asset (looks like :filename without suffix)
# input[:data] # => CoffeeScript source (after other processing; possibly ERB, etc.)
def self.call(input)
data = input[:data]
compiled = input[:cache].fetch([self.cache_key, data]) do
Autoload::CoffeeScript.compile(data, { sourceMap: true }) # Hash key must be cased as expected by CoffeeScript
end
# Investigate `compiled`, it contains several versions of source maps.
# Only write a sourcemap if the JavaScript file has content remaining
# after Sprockets processing (i.e., require directives, etc. have been
# removed. Sprockets leaves behind blank lines after removing directives,
# therefore strip the processed output.
if ! data.strip.empty?
sourcemap = JSON.parse(compiled["v3SourceMap"]) # => {"version"=>3, "file"=>"", "sourceRoot"=>"", "sources"=>[""], "names"=>[], "mappings"=>"AAAA;;AAAA"}
# Investigate values for "names", "file", "sourceRoot".
sourcemap["sources"] = [input[:name] + '.coffee']
sourcemap["sourcesContent"] = [data] # Optional: not needed if the `.coffee` files are available on the server; but placeholder nil required?
# sourcemap["sourceRoot"] = ["sourcemaps"] # Optional: server path to sourcemaps root, if location is different
# sourcemap["names"] = [] # Symbol names used by the mappings entry.
sourcemap_json = sourcemap.to_json
# Write the sourcemap to the Blade build path.
sourcemap_filename = "#{input[:name]}-#{digest(sourcemap_json)}.coffee.map"
sourcemap_path = File.join(Blade.config.build.path, sourcemap_filename)
FileUtils.mkdir_p File.dirname(sourcemap_path)
File.open(sourcemap_path, 'w') { |f| f.write sourcemap_json }
compiled["js"].concat "\n//# sourceMappingURL=#{sourcemap_filename}\n"
else
compiled["js"] # => JS after CS compilation
end
end
def self.digest(io)
Sprockets::DigestUtils.pack_hexdigest Sprockets::DigestUtils.digest(io)
end
end
end
(-> throw Error)()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment