sprockets 3.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +76 -0
- data/README.md +426 -404
- data/bin/sprockets +12 -7
- data/lib/rake/sprocketstask.rb +3 -2
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +33 -24
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +1 -0
- data/lib/sprockets/autoload/coffee_script.rb +1 -0
- data/lib/sprockets/autoload/eco.rb +1 -0
- data/lib/sprockets/autoload/ejs.rb +1 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +1 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +1 -0
- data/lib/sprockets/autoload/yui.rb +1 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/autoload.rb +5 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +61 -13
- data/lib/sprockets/bower.rb +5 -2
- data/lib/sprockets/bundle.rb +44 -4
- data/lib/sprockets/cache/file_store.rb +32 -7
- data/lib/sprockets/cache/memory_store.rb +9 -0
- data/lib/sprockets/cache/null_store.rb +8 -0
- data/lib/sprockets/cache.rb +42 -5
- data/lib/sprockets/cached_environment.rb +14 -19
- data/lib/sprockets/closure_compressor.rb +6 -11
- data/lib/sprockets/coffee_script_processor.rb +19 -5
- data/lib/sprockets/compressing.rb +62 -2
- data/lib/sprockets/configuration.rb +3 -7
- data/lib/sprockets/context.rb +98 -23
- data/lib/sprockets/dependencies.rb +9 -8
- data/lib/sprockets/digest_utils.rb +104 -60
- data/lib/sprockets/directive_processor.rb +45 -35
- data/lib/sprockets/eco_processor.rb +3 -2
- data/lib/sprockets/ejs_processor.rb +3 -2
- data/lib/sprockets/encoding_utils.rb +8 -4
- data/lib/sprockets/environment.rb +9 -4
- data/lib/sprockets/erb_processor.rb +28 -21
- data/lib/sprockets/errors.rb +1 -1
- data/lib/sprockets/exporters/base.rb +71 -0
- data/lib/sprockets/exporters/file_exporter.rb +24 -0
- data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
- data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
- data/lib/sprockets/exporting.rb +73 -0
- data/lib/sprockets/file_reader.rb +1 -0
- data/lib/sprockets/http_utils.rb +26 -6
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +11 -10
- data/lib/sprockets/loader.rb +239 -70
- data/lib/sprockets/manifest.rb +97 -44
- data/lib/sprockets/manifest_utils.rb +9 -6
- data/lib/sprockets/mime.rb +8 -42
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +3 -11
- data/lib/sprockets/path_digest_utils.rb +2 -1
- data/lib/sprockets/path_utils.rb +106 -21
- data/lib/sprockets/paths.rb +1 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +31 -51
- data/lib/sprockets/processor_utils.rb +81 -15
- data/lib/sprockets/resolve.rb +182 -95
- data/lib/sprockets/sass_cache_store.rb +1 -0
- data/lib/sprockets/sass_compressor.rb +21 -17
- data/lib/sprockets/sass_functions.rb +1 -0
- data/lib/sprockets/sass_importer.rb +1 -0
- data/lib/sprockets/sass_processor.rb +45 -17
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +57 -34
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +63 -35
- data/lib/sprockets/uglifier_compressor.rb +23 -20
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +15 -14
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +43 -59
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +5 -14
- data/lib/sprockets.rb +103 -33
- metadata +151 -22
- data/LICENSE +0 -21
- data/lib/sprockets/coffee_script_template.rb +0 -6
- data/lib/sprockets/eco_template.rb +0 -6
- data/lib/sprockets/ejs_template.rb +0 -6
- data/lib/sprockets/engines.rb +0 -81
- data/lib/sprockets/erb_template.rb +0 -6
- data/lib/sprockets/legacy.rb +0 -314
- data/lib/sprockets/legacy_proc_processor.rb +0 -35
- data/lib/sprockets/legacy_tilt_processor.rb +0 -29
- data/lib/sprockets/sass_template.rb +0 -7
    
        data/lib/sprockets/context.rb
    CHANGED
    
    | @@ -1,10 +1,9 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 | 
             
            require 'rack/utils'
         | 
| 3 3 | 
             
            require 'set'
         | 
| 4 4 | 
             
            require 'sprockets/errors'
         | 
| 5 5 |  | 
| 6 6 | 
             
            module Sprockets
         | 
| 7 | 
            -
              # Deprecated: `Context` provides helper methods to all processors.
         | 
| 8 7 | 
             
              # They are typically accessed by ERB templates. You can mix in custom helpers
         | 
| 9 8 | 
             
              # by injecting them into `Environment#context_class`. Do not mix them into
         | 
| 10 9 | 
             
              # `Context` directly.
         | 
| @@ -19,10 +18,25 @@ module Sprockets | |
| 19 18 | 
             
              # The `Context` also collects dependencies declared by
         | 
| 20 19 | 
             
              # assets. See `DirectiveProcessor` for an example of this.
         | 
| 21 20 | 
             
              class Context
         | 
| 22 | 
            -
                 | 
| 21 | 
            +
                # Internal: Proxy for ENV that keeps track of the environment variables used
         | 
| 22 | 
            +
                class ENVProxy < SimpleDelegator
         | 
| 23 | 
            +
                  def initialize(context)
         | 
| 24 | 
            +
                    @context = context
         | 
| 25 | 
            +
                    super(ENV)
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def [](key)
         | 
| 29 | 
            +
                    @context.depend_on_env(key)
         | 
| 30 | 
            +
                    super
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def fetch(key, *)
         | 
| 34 | 
            +
                    @context.depend_on_env(key)
         | 
| 35 | 
            +
                    super
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 23 38 |  | 
| 24 | 
            -
                 | 
| 25 | 
            -
                attr_accessor :__LINE__
         | 
| 39 | 
            +
                attr_reader :environment, :filename
         | 
| 26 40 |  | 
| 27 41 | 
             
                def initialize(input)
         | 
| 28 42 | 
             
                  @environment  = input[:environment]
         | 
| @@ -31,7 +45,6 @@ module Sprockets | |
| 31 45 | 
             
                  @logical_path = input[:name]
         | 
| 32 46 | 
             
                  @filename     = input[:filename]
         | 
| 33 47 | 
             
                  @dirname      = File.dirname(@filename)
         | 
| 34 | 
            -
                  @pathname     = Pathname.new(@filename)
         | 
| 35 48 | 
             
                  @content_type = input[:content_type]
         | 
| 36 49 |  | 
| 37 50 | 
             
                  @required     = Set.new(@metadata[:required])
         | 
| @@ -47,6 +60,10 @@ module Sprockets | |
| 47 60 | 
             
                    dependencies: @dependencies }
         | 
| 48 61 | 
             
                end
         | 
| 49 62 |  | 
| 63 | 
            +
                def env_proxy
         | 
| 64 | 
            +
                  ENVProxy.new(self)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 50 67 | 
             
                # Returns the environment path that contains the file.
         | 
| 51 68 | 
             
                #
         | 
| 52 69 | 
             
                # If `app/javascripts` and `app/stylesheets` are in your path, and
         | 
| @@ -79,13 +96,13 @@ module Sprockets | |
| 79 96 | 
             
                #     resolve("./bar.js")
         | 
| 80 97 | 
             
                #     # => "file:///path/to/app/javascripts/bar.js?type=application/javascript"
         | 
| 81 98 | 
             
                #
         | 
| 82 | 
            -
                # path | 
| 83 | 
            -
                #  | 
| 84 | 
            -
                #   accept - String content accept type
         | 
| 99 | 
            +
                # path   - String logical or absolute path
         | 
| 100 | 
            +
                # accept - String content accept type
         | 
| 85 101 | 
             
                #
         | 
| 86 102 | 
             
                # Returns an Asset URI String.
         | 
| 87 | 
            -
                def resolve(path,  | 
| 88 | 
            -
                   | 
| 103 | 
            +
                def resolve(path, **kargs)
         | 
| 104 | 
            +
                  kargs[:base_path] = @dirname
         | 
| 105 | 
            +
                  uri, deps = environment.resolve!(path, **kargs)
         | 
| 89 106 | 
             
                  @dependencies.merge(deps)
         | 
| 90 107 | 
             
                  uri
         | 
| 91 108 | 
             
                end
         | 
| @@ -105,13 +122,13 @@ module Sprockets | |
| 105 122 | 
             
                # including it.
         | 
| 106 123 | 
             
                #
         | 
| 107 124 | 
             
                # This is used for caching purposes. Any changes made to
         | 
| 108 | 
            -
                # the dependency file  | 
| 125 | 
            +
                # the dependency file will invalidate the cache of the
         | 
| 109 126 | 
             
                # source file.
         | 
| 110 127 | 
             
                def depend_on(path)
         | 
| 111 | 
            -
                  if environment.absolute_path?(path) && environment. | 
| 128 | 
            +
                  if environment.absolute_path?(path) && environment.stat(path)
         | 
| 112 129 | 
             
                    @dependencies << environment.build_file_digest_uri(path)
         | 
| 113 130 | 
             
                  else
         | 
| 114 | 
            -
                    resolve(path | 
| 131 | 
            +
                    resolve(path)
         | 
| 115 132 | 
             
                  end
         | 
| 116 133 | 
             
                  nil
         | 
| 117 134 | 
             
                end
         | 
| @@ -121,10 +138,19 @@ module Sprockets | |
| 121 138 | 
             
                #
         | 
| 122 139 | 
             
                # This is used for caching purposes. Any changes that would
         | 
| 123 140 | 
             
                # invalidate the dependency asset will invalidate the source
         | 
| 124 | 
            -
                # file. Unlike `depend_on`, this will  | 
| 141 | 
            +
                # file. Unlike `depend_on`, this will recursively include
         | 
| 125 142 | 
             
                # the target asset's dependencies.
         | 
| 126 143 | 
             
                def depend_on_asset(path)
         | 
| 127 | 
            -
                  load(resolve(path | 
| 144 | 
            +
                  load(resolve(path))
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                # `depend_on_env` allows you to state a dependency on an environment
         | 
| 148 | 
            +
                # variable.
         | 
| 149 | 
            +
                #
         | 
| 150 | 
            +
                # This is used for caching purposes. Any changes in the value of the
         | 
| 151 | 
            +
                # environment variable will invalidate the cache of the source file.
         | 
| 152 | 
            +
                def depend_on_env(key)
         | 
| 153 | 
            +
                  @dependencies << "env:#{key}"
         | 
| 128 154 | 
             
                end
         | 
| 129 155 |  | 
| 130 156 | 
             
                # `require_asset` declares `path` as a dependency of the file. The
         | 
| @@ -137,7 +163,7 @@ module Sprockets | |
| 137 163 | 
             
                #     <%= require_asset "#{framework}.js" %>
         | 
| 138 164 | 
             
                #
         | 
| 139 165 | 
             
                def require_asset(path)
         | 
| 140 | 
            -
                  @required << resolve(path, accept: @content_type, pipeline: :self | 
| 166 | 
            +
                  @required << resolve(path, accept: @content_type, pipeline: :self)
         | 
| 141 167 | 
             
                  nil
         | 
| 142 168 | 
             
                end
         | 
| 143 169 |  | 
| @@ -145,7 +171,7 @@ module Sprockets | |
| 145 171 | 
             
                # `path` must be an asset which may or may not already be included
         | 
| 146 172 | 
             
                # in the bundle.
         | 
| 147 173 | 
             
                def stub_asset(path)
         | 
| 148 | 
            -
                  @stubbed << resolve(path, accept: @content_type, pipeline: :self | 
| 174 | 
            +
                  @stubbed << resolve(path, accept: @content_type, pipeline: :self)
         | 
| 149 175 | 
             
                  nil
         | 
| 150 176 | 
             
                end
         | 
| 151 177 |  | 
| @@ -160,9 +186,10 @@ module Sprockets | |
| 160 186 | 
             
                  asset
         | 
| 161 187 | 
             
                end
         | 
| 162 188 |  | 
| 163 | 
            -
                # Returns a  | 
| 164 | 
            -
                #  | 
| 165 | 
            -
                # | 
| 189 | 
            +
                # Returns a `data:` URI with the contents of the asset at the specified
         | 
| 190 | 
            +
                # path, and marks that path as a dependency of the current file.
         | 
| 191 | 
            +
                #
         | 
| 192 | 
            +
                # Uses URI encoding for SVG files, base64 encoding for all the other files.
         | 
| 166 193 | 
             
                #
         | 
| 167 194 | 
             
                # Use `asset_data_uri` from ERB with CSS or JavaScript assets:
         | 
| 168 195 | 
             
                #
         | 
| @@ -172,8 +199,11 @@ module Sprockets | |
| 172 199 | 
             
                #
         | 
| 173 200 | 
             
                def asset_data_uri(path)
         | 
| 174 201 | 
             
                  asset = depend_on_asset(path)
         | 
| 175 | 
            -
                   | 
| 176 | 
            -
             | 
| 202 | 
            +
                  if asset.content_type == 'image/svg+xml'
         | 
| 203 | 
            +
                    svg_asset_data_uri(asset)
         | 
| 204 | 
            +
                  else
         | 
| 205 | 
            +
                    base64_asset_data_uri(asset)
         | 
| 206 | 
            +
                  end
         | 
| 177 207 | 
             
                end
         | 
| 178 208 |  | 
| 179 209 | 
             
                # Expands logical path to full url to asset.
         | 
| @@ -225,5 +255,50 @@ Extend your environment context with a custom method. | |
| 225 255 | 
             
                def stylesheet_path(path)
         | 
| 226 256 | 
             
                  asset_path(path, type: :stylesheet)
         | 
| 227 257 | 
             
                end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                protected
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                # Returns a URI-encoded data URI (always "-quoted).
         | 
| 262 | 
            +
                def svg_asset_data_uri(asset)
         | 
| 263 | 
            +
                  svg = asset.source.dup
         | 
| 264 | 
            +
                  optimize_svg_for_uri_escaping!(svg)
         | 
| 265 | 
            +
                  data = Rack::Utils.escape(svg)
         | 
| 266 | 
            +
                  optimize_quoted_uri_escapes!(data)
         | 
| 267 | 
            +
                  "\"data:#{asset.content_type};charset=utf-8,#{data}\""
         | 
| 268 | 
            +
                end
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                # Returns a Base64-encoded data URI.
         | 
| 271 | 
            +
                def base64_asset_data_uri(asset)
         | 
| 272 | 
            +
                  data = Rack::Utils.escape(EncodingUtils.base64(asset.source))
         | 
| 273 | 
            +
                  "data:#{asset.content_type};base64,#{data}"
         | 
| 274 | 
            +
                end
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                # Optimizes an SVG for being URI-escaped.
         | 
| 277 | 
            +
                #
         | 
| 278 | 
            +
                # This method only performs these basic but crucial optimizations:
         | 
| 279 | 
            +
                # * Replaces " with ', because ' does not need escaping.
         | 
| 280 | 
            +
                # * Removes comments, meta, doctype, and newlines.
         | 
| 281 | 
            +
                # * Collapses whitespace.
         | 
| 282 | 
            +
                def optimize_svg_for_uri_escaping!(svg)
         | 
| 283 | 
            +
                  # Remove comments, xml meta, and doctype
         | 
| 284 | 
            +
                  svg.gsub!(/<!--.*?-->|<\?.*?\?>|<!.*?>/m, '')
         | 
| 285 | 
            +
                  # Replace consecutive whitespace and newlines with a space
         | 
| 286 | 
            +
                  svg.gsub!(/\s+/, ' ')
         | 
| 287 | 
            +
                  # Collapse inter-tag whitespace
         | 
| 288 | 
            +
                  svg.gsub!('> <', '><')
         | 
| 289 | 
            +
                  # Replace " with '
         | 
| 290 | 
            +
                  svg.gsub!(/([\w:])="(.*?)"/, "\\1='\\2'")
         | 
| 291 | 
            +
                  svg.strip!
         | 
| 292 | 
            +
                end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                # Un-escapes characters in the given URI-escaped string that do not need
         | 
| 295 | 
            +
                # escaping in "-quoted data URIs.
         | 
| 296 | 
            +
                def optimize_quoted_uri_escapes!(escaped)
         | 
| 297 | 
            +
                  escaped.gsub!('%3D', '=')
         | 
| 298 | 
            +
                  escaped.gsub!('%3A', ':')
         | 
| 299 | 
            +
                  escaped.gsub!('%2F', '/')
         | 
| 300 | 
            +
                  escaped.gsub!('%27', "'")
         | 
| 301 | 
            +
                  escaped.tr!('+', ' ')
         | 
| 302 | 
            +
                end
         | 
| 228 303 | 
             
              end
         | 
| 229 304 | 
             
            end
         | 
| @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'sprockets/digest_utils'
         | 
| 2 3 | 
             
            require 'sprockets/path_digest_utils'
         | 
| 3 4 | 
             
            require 'sprockets/uri_utils'
         | 
| @@ -51,18 +52,18 @@ module Sprockets | |
| 51 52 | 
             
                end
         | 
| 52 53 | 
             
                alias_method :depend_on, :add_dependency
         | 
| 53 54 |  | 
| 54 | 
            -
                # Internal: Resolve set of dependency URIs.
         | 
| 55 | 
            -
                #
         | 
| 56 | 
            -
                # Returns Array of resolved Objects.
         | 
| 57 | 
            -
                def resolve_dependencies(uris)
         | 
| 58 | 
            -
                  uris.map { |uri| resolve_dependency(uri) }
         | 
| 59 | 
            -
                end
         | 
| 60 | 
            -
             | 
| 61 55 | 
             
                # Internal: Resolve dependency URIs.
         | 
| 62 56 | 
             
                #
         | 
| 63 57 | 
             
                # Returns resolved Object.
         | 
| 64 58 | 
             
                def resolve_dependency(str)
         | 
| 65 | 
            -
                   | 
| 59 | 
            +
                  # Optimize for the most common scheme to
         | 
| 60 | 
            +
                  # save 22k allocations on an average Spree app.
         | 
| 61 | 
            +
                  scheme = if str.start_with?('file-digest:'.freeze)
         | 
| 62 | 
            +
                    'file-digest'.freeze
         | 
| 63 | 
            +
                  else
         | 
| 64 | 
            +
                    str[/([^:]+)/, 1]
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 66 67 | 
             
                  if resolver = config[:dependency_resolvers][scheme]
         | 
| 67 68 | 
             
                    resolver.call(self, str)
         | 
| 68 69 | 
             
                  else
         | 
| @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'digest/md5'
         | 
| 2 3 | 
             
            require 'digest/sha1'
         | 
| 3 4 | 
             
            require 'digest/sha2'
         | 
| @@ -34,6 +35,59 @@ module Sprockets | |
| 34 35 | 
             
                  DIGEST_SIZES[bytes.bytesize]
         | 
| 35 36 | 
             
                end
         | 
| 36 37 |  | 
| 38 | 
            +
                ADD_VALUE_TO_DIGEST = {
         | 
| 39 | 
            +
                  String     => ->(val, digest) { digest << val },
         | 
| 40 | 
            +
                  FalseClass => ->(val, digest) { digest << 'FalseClass'.freeze },
         | 
| 41 | 
            +
                  TrueClass  => ->(val, digest) { digest << 'TrueClass'.freeze  },
         | 
| 42 | 
            +
                  NilClass   => ->(val, digest) { digest << 'NilClass'.freeze   },
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  Symbol => ->(val, digest) {
         | 
| 45 | 
            +
                    digest << 'Symbol'.freeze
         | 
| 46 | 
            +
                    digest << val.to_s
         | 
| 47 | 
            +
                  },
         | 
| 48 | 
            +
                  Integer => ->(val, digest) {
         | 
| 49 | 
            +
                    digest << 'Integer'.freeze
         | 
| 50 | 
            +
                    digest << val.to_s
         | 
| 51 | 
            +
                  },
         | 
| 52 | 
            +
                  Array => ->(val, digest) {
         | 
| 53 | 
            +
                    digest << 'Array'.freeze
         | 
| 54 | 
            +
                    val.each do |element|
         | 
| 55 | 
            +
                      ADD_VALUE_TO_DIGEST[element.class].call(element, digest)
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  },
         | 
| 58 | 
            +
                  Hash => ->(val, digest) {
         | 
| 59 | 
            +
                    digest << 'Hash'.freeze
         | 
| 60 | 
            +
                    val.sort.each do |array|
         | 
| 61 | 
            +
                      ADD_VALUE_TO_DIGEST[Array].call(array, digest)
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  },
         | 
| 64 | 
            +
                  Set => ->(val, digest) {
         | 
| 65 | 
            +
                    digest << 'Set'.freeze
         | 
| 66 | 
            +
                    ADD_VALUE_TO_DIGEST[Array].call(val, digest)
         | 
| 67 | 
            +
                  },
         | 
| 68 | 
            +
                  Encoding => ->(val, digest) {
         | 
| 69 | 
            +
                    digest << 'Encoding'.freeze
         | 
| 70 | 
            +
                    digest << val.name
         | 
| 71 | 
            +
                  },
         | 
| 72 | 
            +
                }
         | 
| 73 | 
            +
                if 0.class != Integer # Ruby < 2.4
         | 
| 74 | 
            +
                  ADD_VALUE_TO_DIGEST[Fixnum] = ->(val, digest) {
         | 
| 75 | 
            +
                    digest << 'Integer'.freeze
         | 
| 76 | 
            +
                    digest << val.to_s
         | 
| 77 | 
            +
                  }
         | 
| 78 | 
            +
                  ADD_VALUE_TO_DIGEST[Bignum] = ->(val, digest) {
         | 
| 79 | 
            +
                    digest << 'Integer'.freeze
         | 
| 80 | 
            +
                    digest << val.to_s
         | 
| 81 | 
            +
                  }
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                ADD_VALUE_TO_DIGEST.compare_by_identity.rehash
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) {
         | 
| 87 | 
            +
                  raise TypeError, "couldn't digest #{ val }"
         | 
| 88 | 
            +
                }
         | 
| 89 | 
            +
                private_constant :ADD_VALUE_TO_DIGEST
         | 
| 90 | 
            +
             | 
| 37 91 | 
             
                # Internal: Generate a hexdigest for a nested JSON serializable object.
         | 
| 38 92 | 
             
                #
         | 
| 39 93 | 
             
                # This is used for generating cache keys, so its pretty important its
         | 
| @@ -43,48 +97,18 @@ module Sprockets | |
| 43 97 | 
             
                #
         | 
| 44 98 | 
             
                # Returns a String digest of the object.
         | 
| 45 99 | 
             
                def digest(obj)
         | 
| 46 | 
            -
                  digest | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
                  while queue.length > 0
         | 
| 50 | 
            -
                    obj = queue.shift
         | 
| 51 | 
            -
                    klass = obj.class
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                    if klass == String
         | 
| 54 | 
            -
                      digest << obj
         | 
| 55 | 
            -
                    elsif klass == Symbol
         | 
| 56 | 
            -
                      digest << 'Symbol'
         | 
| 57 | 
            -
                      digest << obj.to_s
         | 
| 58 | 
            -
                    elsif klass == Fixnum
         | 
| 59 | 
            -
                      digest << 'Fixnum'
         | 
| 60 | 
            -
                      digest << obj.to_s
         | 
| 61 | 
            -
                    elsif klass == Bignum
         | 
| 62 | 
            -
                      digest << 'Bignum'
         | 
| 63 | 
            -
                      digest << obj.to_s
         | 
| 64 | 
            -
                    elsif klass == TrueClass
         | 
| 65 | 
            -
                      digest << 'TrueClass'
         | 
| 66 | 
            -
                    elsif klass == FalseClass
         | 
| 67 | 
            -
                      digest << 'FalseClass'
         | 
| 68 | 
            -
                    elsif klass == NilClass
         | 
| 69 | 
            -
                      digest << 'NilClass'
         | 
| 70 | 
            -
                    elsif klass == Array
         | 
| 71 | 
            -
                      digest << 'Array'
         | 
| 72 | 
            -
                      queue.concat(obj)
         | 
| 73 | 
            -
                    elsif klass == Hash
         | 
| 74 | 
            -
                      digest << 'Hash'
         | 
| 75 | 
            -
                      queue.concat(obj.sort)
         | 
| 76 | 
            -
                    elsif klass == Set
         | 
| 77 | 
            -
                      digest << 'Set'
         | 
| 78 | 
            -
                      queue.concat(obj.to_a)
         | 
| 79 | 
            -
                    elsif klass == Encoding
         | 
| 80 | 
            -
                      digest << 'Encoding'
         | 
| 81 | 
            -
                      digest << obj.name
         | 
| 82 | 
            -
                    else
         | 
| 83 | 
            -
                      raise TypeError, "couldn't digest #{klass}"
         | 
| 84 | 
            -
                    end
         | 
| 85 | 
            -
                  end
         | 
| 100 | 
            +
                  build_digest(obj).digest
         | 
| 101 | 
            +
                end
         | 
| 86 102 |  | 
| 87 | 
            -
             | 
| 103 | 
            +
                # Internal: Generate a hexdigest for a nested JSON serializable object.
         | 
| 104 | 
            +
                #
         | 
| 105 | 
            +
                # The same as `pack_hexdigest(digest(obj))`.
         | 
| 106 | 
            +
                #
         | 
| 107 | 
            +
                # obj - A JSON serializable object.
         | 
| 108 | 
            +
                #
         | 
| 109 | 
            +
                # Returns a String digest of the object.
         | 
| 110 | 
            +
                def hexdigest(obj)
         | 
| 111 | 
            +
                  build_digest(obj).hexdigest!
         | 
| 88 112 | 
             
                end
         | 
| 89 113 |  | 
| 90 114 | 
             
                # Internal: Pack a binary digest to a hex encoded string.
         | 
| @@ -93,7 +117,16 @@ module Sprockets | |
| 93 117 | 
             
                #
         | 
| 94 118 | 
             
                # Returns hex String.
         | 
| 95 119 | 
             
                def pack_hexdigest(bin)
         | 
| 96 | 
            -
                  bin.unpack('H*').first
         | 
| 120 | 
            +
                  bin.unpack('H*'.freeze).first
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                # Internal: Unpack a hex encoded digest string into binary bytes.
         | 
| 124 | 
            +
                #
         | 
| 125 | 
            +
                # hex - String hex
         | 
| 126 | 
            +
                #
         | 
| 127 | 
            +
                # Returns binary String.
         | 
| 128 | 
            +
                def unpack_hexdigest(hex)
         | 
| 129 | 
            +
                  [hex].pack('H*')
         | 
| 97 130 | 
             
                end
         | 
| 98 131 |  | 
| 99 132 | 
             
                # Internal: Pack a binary digest to a base64 encoded string.
         | 
| @@ -117,25 +150,20 @@ module Sprockets | |
| 117 150 | 
             
                  str
         | 
| 118 151 | 
             
                end
         | 
| 119 152 |  | 
| 120 | 
            -
                # Internal: Maps digest class to the  | 
| 121 | 
            -
                 | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
                  Digest:: | 
| 125 | 
            -
                  Digest::SHA384 => 'sha-384'.freeze,
         | 
| 126 | 
            -
                  Digest::SHA512 => 'sha-512'.freeze
         | 
| 153 | 
            +
                # Internal: Maps digest class to the CSP hash algorithm name.
         | 
| 154 | 
            +
                HASH_ALGORITHMS = {
         | 
| 155 | 
            +
                  Digest::SHA256 => 'sha256'.freeze,
         | 
| 156 | 
            +
                  Digest::SHA384 => 'sha384'.freeze,
         | 
| 157 | 
            +
                  Digest::SHA512 => 'sha512'.freeze
         | 
| 127 158 | 
             
                }
         | 
| 128 159 |  | 
| 129 | 
            -
                #  | 
| 130 | 
            -
                #  | 
| 160 | 
            +
                # Public: Generate hash for use in the `integrity` attribute of an asset tag
         | 
| 161 | 
            +
                # as per the subresource integrity specification.
         | 
| 131 162 | 
             
                #
         | 
| 132 | 
            -
                # digest | 
| 133 | 
            -
                # content_type - The content-type the asset will be served with. This *must*
         | 
| 134 | 
            -
                #                be accurate if provided. Otherwise, subresource integrity
         | 
| 135 | 
            -
                #                will block the loading of the asset.
         | 
| 163 | 
            +
                # digest - The String byte digest of the asset content.
         | 
| 136 164 | 
             
                #
         | 
| 137 165 | 
             
                # Returns a String or nil if hash algorithm is incompatible.
         | 
| 138 | 
            -
                def integrity_uri(digest | 
| 166 | 
            +
                def integrity_uri(digest)
         | 
| 139 167 | 
             
                  case digest
         | 
| 140 168 | 
             
                  when Digest::Base
         | 
| 141 169 | 
             
                    digest_class = digest.class
         | 
| @@ -146,11 +174,27 @@ module Sprockets | |
| 146 174 | 
             
                    raise TypeError, "unknown digest: #{digest.inspect}"
         | 
| 147 175 | 
             
                  end
         | 
| 148 176 |  | 
| 149 | 
            -
                  if hash_name =  | 
| 150 | 
            -
                     | 
| 151 | 
            -
                    uri << "?ct=#{content_type}" if content_type
         | 
| 152 | 
            -
                    uri
         | 
| 177 | 
            +
                  if hash_name = HASH_ALGORITHMS[digest_class]
         | 
| 178 | 
            +
                    "#{hash_name}-#{pack_base64digest(digest)}"
         | 
| 153 179 | 
             
                  end
         | 
| 154 180 | 
             
                end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                # Public: Generate hash for use in the `integrity` attribute of an asset tag
         | 
| 183 | 
            +
                # as per the subresource integrity specification.
         | 
| 184 | 
            +
                #
         | 
| 185 | 
            +
                # digest - The String hexbyte digest of the asset content.
         | 
| 186 | 
            +
                #
         | 
| 187 | 
            +
                # Returns a String or nil if hash algorithm is incompatible.
         | 
| 188 | 
            +
                def hexdigest_integrity_uri(hexdigest)
         | 
| 189 | 
            +
                  integrity_uri(unpack_hexdigest(hexdigest))
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                private
         | 
| 193 | 
            +
                  def build_digest(obj)
         | 
| 194 | 
            +
                    digest = digest_class.new
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                    ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest)
         | 
| 197 | 
            +
                    digest
         | 
| 198 | 
            +
                  end
         | 
| 155 199 | 
             
              end
         | 
| 156 200 | 
             
            end
         | 
| @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'set'
         | 
| 2 3 | 
             
            require 'shellwords'
         | 
| 3 4 |  | 
| @@ -34,8 +35,6 @@ module Sprockets | |
| 34 35 | 
             
              #     env.register_processor('text/css', MyProcessor)
         | 
| 35 36 | 
             
              #
         | 
| 36 37 | 
             
              class DirectiveProcessor
         | 
| 37 | 
            -
                VERSION = '1'
         | 
| 38 | 
            -
             | 
| 39 38 | 
             
                # Directives are denoted by a `=` followed by the name, then
         | 
| 40 39 | 
             
                # argument list.
         | 
| 41 40 | 
             
                #
         | 
| @@ -50,18 +49,16 @@ module Sprockets | |
| 50 49 | 
             
                /x
         | 
| 51 50 |  | 
| 52 51 | 
             
                def self.instance
         | 
| 53 | 
            -
                   | 
| 54 | 
            -
             | 
| 55 | 
            -
                    comments: ["//", ["/*", "*/"]] + ["#", ["###", "###"]]
         | 
| 56 | 
            -
                  )
         | 
| 52 | 
            +
                  # Default to C comment styles
         | 
| 53 | 
            +
                  @instance ||= new(comments: ["//", ["/*", "*/"]])
         | 
| 57 54 | 
             
                end
         | 
| 58 55 |  | 
| 59 56 | 
             
                def self.call(input)
         | 
| 60 57 | 
             
                  instance.call(input)
         | 
| 61 58 | 
             
                end
         | 
| 62 59 |  | 
| 63 | 
            -
                def initialize( | 
| 64 | 
            -
                  @header_pattern = compile_header_pattern(Array( | 
| 60 | 
            +
                def initialize(comments: [])
         | 
| 61 | 
            +
                  @header_pattern = compile_header_pattern(Array(comments))
         | 
| 65 62 | 
             
                end
         | 
| 66 63 |  | 
| 67 64 | 
             
                def call(input)
         | 
| @@ -73,20 +70,28 @@ module Sprockets | |
| 73 70 | 
             
                  @uri          = input[:uri]
         | 
| 74 71 | 
             
                  @filename     = input[:filename]
         | 
| 75 72 | 
             
                  @dirname      = File.dirname(@filename)
         | 
| 76 | 
            -
                   | 
| 73 | 
            +
                  # If loading a source map file like `application.js.map` resolve
         | 
| 74 | 
            +
                  # dependencies using `.js` instead of `.js.map`
         | 
| 75 | 
            +
                  @content_type = SourceMapProcessor.original_content_type(input[:content_type], error_when_not_found: false)
         | 
| 77 76 | 
             
                  @required     = Set.new(input[:metadata][:required])
         | 
| 78 77 | 
             
                  @stubbed      = Set.new(input[:metadata][:stubbed])
         | 
| 79 78 | 
             
                  @links        = Set.new(input[:metadata][:links])
         | 
| 80 79 | 
             
                  @dependencies = Set.new(input[:metadata][:dependencies])
         | 
| 80 | 
            +
                  @to_link      = Set.new
         | 
| 81 | 
            +
                  @to_load      = Set.new
         | 
| 81 82 |  | 
| 82 83 | 
             
                  data, directives = process_source(input[:data])
         | 
| 83 84 | 
             
                  process_directives(directives)
         | 
| 84 85 |  | 
| 85 | 
            -
                  { | 
| 86 | 
            -
                     | 
| 87 | 
            -
                     | 
| 88 | 
            -
                     | 
| 89 | 
            -
                     | 
| 86 | 
            +
                  {
         | 
| 87 | 
            +
                    data:         data,
         | 
| 88 | 
            +
                    required:     @required,
         | 
| 89 | 
            +
                    stubbed:      @stubbed,
         | 
| 90 | 
            +
                    links:        @links,
         | 
| 91 | 
            +
                    to_load:      @to_load,
         | 
| 92 | 
            +
                    to_link:      @to_link,
         | 
| 93 | 
            +
                    dependencies: @dependencies
         | 
| 94 | 
            +
                  }
         | 
| 90 95 | 
             
                end
         | 
| 91 96 |  | 
| 92 97 | 
             
                protected
         | 
| @@ -116,9 +121,9 @@ module Sprockets | |
| 116 121 |  | 
| 117 122 | 
             
                    header, directives = extract_directives(header)
         | 
| 118 123 |  | 
| 119 | 
            -
                    data = ""
         | 
| 124 | 
            +
                    data = +""
         | 
| 120 125 | 
             
                    data.force_encoding(body.encoding)
         | 
| 121 | 
            -
                    data << header  | 
| 126 | 
            +
                    data << header unless header.empty?
         | 
| 122 127 | 
             
                    data << body
         | 
| 123 128 | 
             
                    # Ensure body ends in a new line
         | 
| 124 129 | 
             
                    data << "\n" if data.length > 0 && data[-1] != "\n"
         | 
| @@ -134,7 +139,7 @@ module Sprockets | |
| 134 139 | 
             
                  #     [[1, "require", "foo"], [2, "require", "bar"]]
         | 
| 135 140 | 
             
                  #
         | 
| 136 141 | 
             
                  def extract_directives(header)
         | 
| 137 | 
            -
                    processed_header = ""
         | 
| 142 | 
            +
                    processed_header = +""
         | 
| 138 143 | 
             
                    directives = []
         | 
| 139 144 |  | 
| 140 145 | 
             
                    header.lines.each_with_index do |line, index|
         | 
| @@ -149,7 +154,11 @@ module Sprockets | |
| 149 154 | 
             
                      processed_header << line
         | 
| 150 155 | 
             
                    end
         | 
| 151 156 |  | 
| 152 | 
            -
                     | 
| 157 | 
            +
                    processed_header.chomp!
         | 
| 158 | 
            +
                    # Ensure header ends in a new line like before it was processed
         | 
| 159 | 
            +
                    processed_header << "\n" if processed_header.length > 0 && header[-1] == "\n"
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    return processed_header, directives
         | 
| 153 162 | 
             
                  end
         | 
| 154 163 |  | 
| 155 164 | 
             
                  # Gathers comment directives in the source and processes them.
         | 
| @@ -162,7 +171,7 @@ module Sprockets | |
| 162 171 | 
             
                  # `process_require_glob_directive`.
         | 
| 163 172 | 
             
                  #
         | 
| 164 173 | 
             
                  #     class DirectiveProcessor < Sprockets::DirectiveProcessor
         | 
| 165 | 
            -
                  #       def process_require_glob_directive
         | 
| 174 | 
            +
                  #       def process_require_glob_directive(glob)
         | 
| 166 175 | 
             
                  #         Dir["#{dirname}/#{glob}"].sort.each do |filename|
         | 
| 167 176 | 
             
                  #           require(filename)
         | 
| 168 177 | 
             
                  #         end
         | 
| @@ -187,7 +196,7 @@ module Sprockets | |
| 187 196 |  | 
| 188 197 | 
             
                  # The `require` directive functions similar to Ruby's own `require`.
         | 
| 189 198 | 
             
                  # It provides a way to declare a dependency on a file in your path
         | 
| 190 | 
            -
                  # and ensures  | 
| 199 | 
            +
                  # and ensures it's only loaded once before the source file.
         | 
| 191 200 | 
             
                  #
         | 
| 192 201 | 
             
                  # `require` works with files in the environment path:
         | 
| 193 202 | 
             
                  #
         | 
| @@ -265,15 +274,15 @@ module Sprockets | |
| 265 274 | 
             
                  # it.
         | 
| 266 275 | 
             
                  #
         | 
| 267 276 | 
             
                  # This is used for caching purposes. Any changes that would
         | 
| 268 | 
            -
                  #  | 
| 269 | 
            -
                  # source file.
         | 
| 277 | 
            +
                  # invalidate the asset dependency will invalidate the cache of
         | 
| 278 | 
            +
                  # the source file.
         | 
| 270 279 | 
             
                  #
         | 
| 271 280 | 
             
                  # Unlike `depend_on`, the path must be a requirable asset.
         | 
| 272 281 | 
             
                  #
         | 
| 273 282 | 
             
                  #     //= depend_on_asset "bar.js"
         | 
| 274 283 | 
             
                  #
         | 
| 275 284 | 
             
                  def process_depend_on_asset_directive(path)
         | 
| 276 | 
            -
                     | 
| 285 | 
            +
                    to_load(resolve(path))
         | 
| 277 286 | 
             
                  end
         | 
| 278 287 |  | 
| 279 288 | 
             
                  # Allows dependency to be excluded from the asset bundle.
         | 
| @@ -297,7 +306,8 @@ module Sprockets | |
| 297 306 | 
             
                  #   /*= link "logo.png" */
         | 
| 298 307 | 
             
                  #
         | 
| 299 308 | 
             
                  def process_link_directive(path)
         | 
| 300 | 
            -
                     | 
| 309 | 
            +
                    uri = to_load(resolve(path))
         | 
| 310 | 
            +
                    @to_link << uri
         | 
| 301 311 | 
             
                  end
         | 
| 302 312 |  | 
| 303 313 | 
             
                  # `link_directory` links all the files inside a single
         | 
| @@ -307,7 +317,7 @@ module Sprockets | |
| 307 317 | 
             
                  #     //= link_directory "./fonts"
         | 
| 308 318 | 
             
                  #
         | 
| 309 319 | 
             
                  # Use caution when linking against JS or CSS assets. Include an explicit
         | 
| 310 | 
            -
                  # extension or content type in these cases
         | 
| 320 | 
            +
                  # extension or content type in these cases.
         | 
| 311 321 | 
             
                  #
         | 
| 312 322 | 
             
                  #     //= link_directory "./scripts" .js
         | 
| 313 323 | 
             
                  #
         | 
| @@ -323,7 +333,7 @@ module Sprockets | |
| 323 333 | 
             
                  #     //= link_tree "./images"
         | 
| 324 334 | 
             
                  #
         | 
| 325 335 | 
             
                  # Use caution when linking against JS or CSS assets. Include an explicit
         | 
| 326 | 
            -
                  # extension or content type in these cases
         | 
| 336 | 
            +
                  # extension or content type in these cases.
         | 
| 327 337 | 
             
                  #
         | 
| 328 338 | 
             
                  #     //= link_tree "./styles" .css
         | 
| 329 339 | 
             
                  #
         | 
| @@ -354,15 +364,15 @@ module Sprockets | |
| 354 364 |  | 
| 355 365 | 
             
                  def link_paths(paths, deps, accept)
         | 
| 356 366 | 
             
                    resolve_paths(paths, deps, accept: accept) do |uri|
         | 
| 357 | 
            -
                      @ | 
| 367 | 
            +
                      @to_link << to_load(uri)
         | 
| 358 368 | 
             
                    end
         | 
| 359 369 | 
             
                  end
         | 
| 360 370 |  | 
| 361 | 
            -
                  def resolve_paths(paths, deps,  | 
| 371 | 
            +
                  def resolve_paths(paths, deps, **kargs)
         | 
| 362 372 | 
             
                    @dependencies.merge(deps)
         | 
| 363 373 | 
             
                    paths.each do |subpath, stat|
         | 
| 364 374 | 
             
                      next if subpath == @filename || stat.directory?
         | 
| 365 | 
            -
                      uri, deps = @environment.resolve(subpath,  | 
| 375 | 
            +
                      uri, deps = @environment.resolve(subpath, **kargs)
         | 
| 366 376 | 
             
                      @dependencies.merge(deps)
         | 
| 367 377 | 
             
                      yield uri if uri
         | 
| 368 378 | 
             
                    end
         | 
| @@ -384,19 +394,19 @@ module Sprockets | |
| 384 394 | 
             
                    end
         | 
| 385 395 | 
             
                  end
         | 
| 386 396 |  | 
| 387 | 
            -
                  def  | 
| 388 | 
            -
                     | 
| 389 | 
            -
                     | 
| 390 | 
            -
                    asset
         | 
| 397 | 
            +
                  def to_load(uri)
         | 
| 398 | 
            +
                    @to_load << uri
         | 
| 399 | 
            +
                    uri
         | 
| 391 400 | 
             
                  end
         | 
| 392 401 |  | 
| 393 | 
            -
                  def resolve(path,  | 
| 402 | 
            +
                  def resolve(path, **kargs)
         | 
| 394 403 | 
             
                    # Prevent absolute paths in directives
         | 
| 395 404 | 
             
                    if @environment.absolute_path?(path)
         | 
| 396 405 | 
             
                      raise FileOutsidePaths, "can't require absolute file: #{path}"
         | 
| 397 406 | 
             
                    end
         | 
| 398 407 |  | 
| 399 | 
            -
                     | 
| 408 | 
            +
                    kargs[:base_path] = @dirname
         | 
| 409 | 
            +
                    uri, deps = @environment.resolve!(path, **kargs)
         | 
| 400 410 | 
             
                    @dependencies.merge(deps)
         | 
| 401 411 | 
             
                    uri
         | 
| 402 412 | 
             
                  end
         | 
| @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require 'sprockets/autoload'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Sprockets
         | 
| @@ -12,7 +13,7 @@ module Sprockets | |
| 12 13 | 
             
                VERSION = '1'
         | 
| 13 14 |  | 
| 14 15 | 
             
                def self.cache_key
         | 
| 15 | 
            -
                  @cache_key ||=  | 
| 16 | 
            +
                  @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze
         | 
| 16 17 | 
             
                end
         | 
| 17 18 |  | 
| 18 19 | 
             
                # Compile template data with Eco compiler.
         | 
| @@ -24,7 +25,7 @@ module Sprockets | |
| 24 25 | 
             
                #
         | 
| 25 26 | 
             
                def self.call(input)
         | 
| 26 27 | 
             
                  data = input[:data]
         | 
| 27 | 
            -
                  input[:cache].fetch(cache_key  | 
| 28 | 
            +
                  input[:cache].fetch([cache_key, data]) do
         | 
| 28 29 | 
             
                    Autoload::Eco.compile(data)
         | 
| 29 30 | 
             
                  end
         | 
| 30 31 | 
             
                end
         |