require "sprockets" # Sprockets extension module Middleman::Sprockets # Setup extension class << self # Once registered def registered(app) # Location of javascripts external to source directory. # @return [Array] # set :js_assets_paths, ["#{root}/assets/javascripts/", "/path/2/external/js/repository/"] # Add class methods to context app.send :include, InstanceMethods require "middleman-sprockets/sass" app.register Middleman::Sprockets::Sass app.after_configuration do helpers JavascriptTagHelper end # Once Middleman is setup app.after_configuration do ::Tilt.register ::Sprockets::EjsTemplate, 'ejs' ::Tilt.register ::Sprockets::EcoTemplate, 'eco' # Add any gems with (vendor|app|.)/assets/javascripts to paths # also add similar directories from project root (like in rails) try_paths = [ %w{ assets }, %w{ app }, %w{ app assets }, %w{ vendor }, %w{ vendor assets }, %w{ lib }, %w{ lib assets } ].inject([]) do |sum, v| sum + [File.join(v, 'javascripts'), File.join(v, 'stylesheets')] end ([root] + ::Middleman.rubygems_latest_specs.map(&:full_gem_path)).each do |root_path| try_paths.map {|p| File.join(root_path, p) }. select {|p| File.directory?(p) }. each {|path| sprockets.append_path(path) } end # Setup Sprockets Sass options sass.each { |k, v| ::Sprockets::Sass.options[k] = v } # Intercept requests to /javascripts and /stylesheets and pass to sprockets our_sprockets = sprockets map("/#{js_dir}") { run our_sprockets } map("/#{css_dir}") { run our_sprockets } end end alias :included :registered end module InstanceMethods # @return [Middleman::CoreExtensions::Sprockets::MiddlemanSprocketsEnvironment] def sprockets @sprockets ||= MiddlemanSprocketsEnvironment.new(self) end end # Generic Middleman Sprockets env class MiddlemanSprocketsEnvironment < ::Sprockets::Environment # Setup def initialize(app) @app = app super app.source_dir # Make the app context available to Sprockets context_class.send(:define_method, :app) { app } context_class.class_eval do def method_missing(*args) name = args.first if app.respond_to?(name) app.send(*args) else super end end end # Remove compressors, we handle these with middleware unregister_bundle_processor 'application/javascript', :js_compressor unregister_bundle_processor 'text/css', :css_compressor # configure search paths append_path app.js_dir append_path app.css_dir # add custom assets paths to the scope app.js_assets_paths.each do |p| append_path p end if app.respond_to?(:js_assets_paths) end # Override Sprockets' default digest function to *not* # change depending on the exact Sprockets version. It still takes # into account "version" which is a user-suppliable version # number that can be used to force assets to have a new # hash. def digest @digest ||= Digest::SHA1.new.update(version.to_s) @digest.dup end # During development, don't use the asset cache def find_asset(path, options = {}) expire_index! if @app.development? super end # Clear cache on error def javascript_exception_response(exception) expire_index! super(exception) end # Clear cache on error alias :css_exception_response :javascript_exception_response end module JavascriptTagHelper # extend padrinos javascript_include_tag with debug functionality # splits up script dependencies in individual files when # configuration variable :debug_assets is set to true def javascript_include_tag(*sources) if respond_to?(:debug_assets) && debug_assets options = sources.extract_options!.symbolize_keys # loop through all sources and the dependencies and # output each as script tag in the correct order sources.map do |source| dependencies_paths = sprockets[source].to_a.map do |dependency| # if sprockets sees "?body=1" it only gives back the body # of the script without the dependencies included dependency.logical_path << "?body=1" end super(dependencies_paths, options) end.join("") else super end end end end