sprockets 4.0.1

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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +72 -0
  3. data/README.md +665 -0
  4. data/bin/sprockets +93 -0
  5. data/lib/rake/sprocketstask.rb +153 -0
  6. data/lib/sprockets.rb +229 -0
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +202 -0
  9. data/lib/sprockets/autoload.rb +16 -0
  10. data/lib/sprockets/autoload/babel.rb +8 -0
  11. data/lib/sprockets/autoload/closure.rb +8 -0
  12. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  13. data/lib/sprockets/autoload/eco.rb +8 -0
  14. data/lib/sprockets/autoload/ejs.rb +8 -0
  15. data/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/lib/sprockets/autoload/sass.rb +8 -0
  17. data/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/lib/sprockets/autoload/uglifier.rb +8 -0
  19. data/lib/sprockets/autoload/yui.rb +8 -0
  20. data/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +147 -0
  23. data/lib/sprockets/bower.rb +61 -0
  24. data/lib/sprockets/bundle.rb +105 -0
  25. data/lib/sprockets/cache.rb +271 -0
  26. data/lib/sprockets/cache/file_store.rb +208 -0
  27. data/lib/sprockets/cache/memory_store.rb +75 -0
  28. data/lib/sprockets/cache/null_store.rb +54 -0
  29. data/lib/sprockets/cached_environment.rb +64 -0
  30. data/lib/sprockets/closure_compressor.rb +48 -0
  31. data/lib/sprockets/coffee_script_processor.rb +39 -0
  32. data/lib/sprockets/compressing.rb +134 -0
  33. data/lib/sprockets/configuration.rb +79 -0
  34. data/lib/sprockets/context.rb +304 -0
  35. data/lib/sprockets/dependencies.rb +74 -0
  36. data/lib/sprockets/digest_utils.rb +200 -0
  37. data/lib/sprockets/directive_processor.rb +414 -0
  38. data/lib/sprockets/eco_processor.rb +33 -0
  39. data/lib/sprockets/ejs_processor.rb +32 -0
  40. data/lib/sprockets/encoding_utils.rb +262 -0
  41. data/lib/sprockets/environment.rb +46 -0
  42. data/lib/sprockets/erb_processor.rb +37 -0
  43. data/lib/sprockets/errors.rb +12 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +16 -0
  50. data/lib/sprockets/http_utils.rb +135 -0
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +50 -0
  53. data/lib/sprockets/loader.rb +345 -0
  54. data/lib/sprockets/manifest.rb +338 -0
  55. data/lib/sprockets/manifest_utils.rb +48 -0
  56. data/lib/sprockets/mime.rb +96 -0
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +77 -0
  59. data/lib/sprockets/path_digest_utils.rb +48 -0
  60. data/lib/sprockets/path_utils.rb +367 -0
  61. data/lib/sprockets/paths.rb +82 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +228 -0
  64. data/lib/sprockets/processor_utils.rb +169 -0
  65. data/lib/sprockets/resolve.rb +295 -0
  66. data/lib/sprockets/sass_cache_store.rb +30 -0
  67. data/lib/sprockets/sass_compressor.rb +63 -0
  68. data/lib/sprockets/sass_functions.rb +3 -0
  69. data/lib/sprockets/sass_importer.rb +3 -0
  70. data/lib/sprockets/sass_processor.rb +313 -0
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +295 -0
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +173 -0
  77. data/lib/sprockets/uglifier_compressor.rb +66 -0
  78. data/lib/sprockets/unloaded_asset.rb +139 -0
  79. data/lib/sprockets/uri_tar.rb +99 -0
  80. data/lib/sprockets/uri_utils.rb +191 -0
  81. data/lib/sprockets/utils.rb +202 -0
  82. data/lib/sprockets/utils/gzip.rb +99 -0
  83. data/lib/sprockets/version.rb +4 -0
  84. data/lib/sprockets/yui_compressor.rb +56 -0
  85. metadata +444 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/path_utils'
3
+ require 'sprockets/utils'
4
+
5
+ module Sprockets
6
+ module Paths
7
+ include PathUtils, Utils
8
+
9
+ # Returns `Environment` root.
10
+ #
11
+ # All relative paths are expanded with root as its base. To be
12
+ # useful set this to your applications root directory. (`Rails.root`)
13
+ def root
14
+ config[:root]
15
+ end
16
+
17
+ # Internal: Change Environment root.
18
+ #
19
+ # Only the initializer should change the root.
20
+ def root=(path)
21
+ self.config = hash_reassoc(config, :root) do
22
+ File.expand_path(path)
23
+ end
24
+ end
25
+ private :root=
26
+
27
+ # Returns an `Array` of path `String`s.
28
+ #
29
+ # These paths will be used for asset logical path lookups.
30
+ def paths
31
+ config[:paths]
32
+ end
33
+
34
+ # Prepend a `path` to the `paths` list.
35
+ #
36
+ # Paths at the end of the `Array` have the least priority.
37
+ def prepend_path(path)
38
+ self.config = hash_reassoc(config, :paths) do |paths|
39
+ path = File.expand_path(path, config[:root]).freeze
40
+ paths.unshift(path)
41
+ end
42
+ end
43
+
44
+ # Append a `path` to the `paths` list.
45
+ #
46
+ # Paths at the beginning of the `Array` have a higher priority.
47
+ def append_path(path)
48
+ self.config = hash_reassoc(config, :paths) do |paths|
49
+ path = File.expand_path(path, config[:root]).freeze
50
+ paths.push(path)
51
+ end
52
+ end
53
+
54
+ # Clear all paths and start fresh.
55
+ #
56
+ # There is no mechanism for reordering paths, so its best to
57
+ # completely wipe the paths list and reappend them in the order
58
+ # you want.
59
+ def clear_paths
60
+ self.config = hash_reassoc(config, :paths) do |paths|
61
+ paths.clear
62
+ end
63
+ end
64
+
65
+ # Public: Iterate over every file under all load paths.
66
+ #
67
+ # Returns Enumerator if no block is given.
68
+ def each_file
69
+ return to_enum(__method__) unless block_given?
70
+
71
+ paths.each do |root|
72
+ stat_tree(root).each do |filename, stat|
73
+ if stat.file?
74
+ yield filename
75
+ end
76
+ end
77
+ end
78
+
79
+ nil
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ module Preprocessors
4
+ # Private: Adds a default map to assets when one is not present
5
+ #
6
+ # If the input file already has a source map, it effectively returns the original
7
+ # result. Otherwise it maps 1 for 1 lines original to generated. This is needed
8
+ # Because other generators run after might depend on having a valid source map
9
+ # available.
10
+ class DefaultSourceMap
11
+ def call(input)
12
+ result = { data: input[:data] }
13
+ map = input[:metadata][:map]
14
+ filename = input[:filename]
15
+ load_path = input[:load_path]
16
+ lines = input[:data].lines.length
17
+ basename = File.basename(filename)
18
+ mime_exts = input[:environment].config[:mime_exts]
19
+ pipeline_exts = input[:environment].config[:pipeline_exts]
20
+ if map.nil? || map.empty?
21
+ result[:map] = {
22
+ "version" => 3,
23
+ "file" => PathUtils.split_subpath(load_path, filename),
24
+ "mappings" => default_mappings(lines),
25
+ "sources" => [PathUtils.set_pipeline(basename, mime_exts, pipeline_exts, :source)],
26
+ "names" => []
27
+ }
28
+ else
29
+ result[:map] = map
30
+ end
31
+
32
+ result[:map]["x_sprockets_linecount"] = lines
33
+ return result
34
+ end
35
+
36
+ private
37
+
38
+ def default_mappings(lines)
39
+ if (lines == 0)
40
+ ""
41
+ elsif (lines == 1)
42
+ "AAAA"
43
+ else
44
+ "AAAA;" + "AACA;"*(lines - 2) + "AACA"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/file_reader'
3
+ require 'sprockets/mime'
4
+ require 'sprockets/processor_utils'
5
+ require 'sprockets/uri_utils'
6
+ require 'sprockets/utils'
7
+
8
+ module Sprockets
9
+ # `Processing` is an internal mixin whose public methods are exposed on
10
+ # the `Environment` and `CachedEnvironment` classes.
11
+ module Processing
12
+ include ProcessorUtils, URIUtils, Utils
13
+
14
+ def pipelines
15
+ config[:pipelines]
16
+ end
17
+
18
+ # Registers a pipeline that will be called by `call_processor` method.
19
+ def register_pipeline(name, proc = nil, &block)
20
+ proc ||= block
21
+
22
+ self.config = hash_reassoc(config, :pipeline_exts) do |pipeline_exts|
23
+ pipeline_exts.merge(".#{name}".freeze => name.to_sym)
24
+ end
25
+
26
+ self.config = hash_reassoc(config, :pipelines) do |pipelines|
27
+ pipelines.merge(name.to_sym => proc)
28
+ end
29
+ end
30
+
31
+ # Preprocessors are ran before Postprocessors and Engine
32
+ # processors.
33
+ def preprocessors
34
+ config[:preprocessors]
35
+ end
36
+ alias_method :processors, :preprocessors
37
+
38
+ # Postprocessors are ran after Preprocessors and Engine processors.
39
+ def postprocessors
40
+ config[:postprocessors]
41
+ end
42
+
43
+ # Registers a new Preprocessor `klass` for `mime_type`.
44
+ #
45
+ # register_preprocessor 'text/css', Sprockets::DirectiveProcessor
46
+ #
47
+ # A block can be passed for to create a shorthand processor.
48
+ #
49
+ # register_preprocessor 'text/css' do |input|
50
+ # input[:data].gsub(...)
51
+ # end
52
+ #
53
+ def register_preprocessor(*args, &block)
54
+ register_config_processor(:preprocessors, *args, &block)
55
+ compute_transformers!(self.config[:registered_transformers])
56
+ end
57
+ alias_method :register_processor, :register_preprocessor
58
+
59
+ # Registers a new Postprocessor `klass` for `mime_type`.
60
+ #
61
+ # register_postprocessor 'application/javascript', Sprockets::DirectiveProcessor
62
+ #
63
+ # A block can be passed for to create a shorthand processor.
64
+ #
65
+ # register_postprocessor 'application/javascript' do |input|
66
+ # input[:data].gsub(...)
67
+ # end
68
+ #
69
+ def register_postprocessor(*args, &block)
70
+ register_config_processor(:postprocessors, *args, &block)
71
+ compute_transformers!(self.config[:registered_transformers])
72
+ end
73
+
74
+ # Remove Preprocessor `klass` for `mime_type`.
75
+ #
76
+ # unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
77
+ #
78
+ def unregister_preprocessor(*args)
79
+ unregister_config_processor(:preprocessors, *args)
80
+ compute_transformers!(self.config[:registered_transformers])
81
+ end
82
+ alias_method :unregister_processor, :unregister_preprocessor
83
+
84
+ # Remove Postprocessor `klass` for `mime_type`.
85
+ #
86
+ # unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
87
+ #
88
+ def unregister_postprocessor(*args)
89
+ unregister_config_processor(:postprocessors, *args)
90
+ compute_transformers!(self.config[:registered_transformers])
91
+ end
92
+
93
+ # Bundle Processors are ran on concatenated assets rather than
94
+ # individual files.
95
+ def bundle_processors
96
+ config[:bundle_processors]
97
+ end
98
+
99
+ # Registers a new Bundle Processor `klass` for `mime_type`.
100
+ #
101
+ # register_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
102
+ #
103
+ # A block can be passed for to create a shorthand processor.
104
+ #
105
+ # register_bundle_processor 'application/javascript' do |input|
106
+ # input[:data].gsub(...)
107
+ # end
108
+ #
109
+ def register_bundle_processor(*args, &block)
110
+ register_config_processor(:bundle_processors, *args, &block)
111
+ end
112
+
113
+ # Remove Bundle Processor `klass` for `mime_type`.
114
+ #
115
+ # unregister_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor
116
+ #
117
+ def unregister_bundle_processor(*args)
118
+ unregister_config_processor(:bundle_processors, *args)
119
+ end
120
+
121
+ # Public: Register bundle metadata reducer function.
122
+ #
123
+ # Examples
124
+ #
125
+ # Sprockets.register_bundle_metadata_reducer 'application/javascript', :jshint_errors, [], :+
126
+ #
127
+ # Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, 0 { |total, count|
128
+ # total + count
129
+ # }
130
+ #
131
+ # mime_type - String MIME Type. Use '*/*' applies to all types.
132
+ # key - Symbol metadata key
133
+ # initial - Initial memo to pass to the reduce funciton (default: nil)
134
+ # block - Proc accepting the memo accumulator and current value
135
+ #
136
+ # Returns nothing.
137
+ def register_bundle_metadata_reducer(mime_type, key, *args, &block)
138
+ case args.size
139
+ when 0
140
+ reducer = block
141
+ when 1
142
+ if block_given?
143
+ initial = args[0]
144
+ reducer = block
145
+ else
146
+ initial = nil
147
+ reducer = args[0].to_proc
148
+ end
149
+ when 2
150
+ initial = args[0]
151
+ reducer = args[1].to_proc
152
+ else
153
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)"
154
+ end
155
+
156
+ self.config = hash_reassoc(config, :bundle_reducers, mime_type) do |reducers|
157
+ reducers.merge(key => [initial, reducer])
158
+ end
159
+ end
160
+
161
+ protected
162
+ def resolve_processors_cache_key_uri(uri)
163
+ params = parse_uri_query_params(uri[11..-1])
164
+ processors = processors_for(params[:type], params[:file_type], params[:pipeline])
165
+ processors_cache_keys(processors)
166
+ end
167
+
168
+ def build_processors_uri(type, file_type, pipeline)
169
+ query = encode_uri_query_params(
170
+ type: type,
171
+ file_type: file_type,
172
+ pipeline: pipeline
173
+ )
174
+ "processors:#{query}"
175
+ end
176
+
177
+ def processors_for(type, file_type, pipeline)
178
+ pipeline ||= :default
179
+ if fn = config[:pipelines][pipeline.to_sym]
180
+ fn.call(self, type, file_type)
181
+ else
182
+ raise Error, "no pipeline: #{pipeline}"
183
+ end
184
+ end
185
+
186
+ def default_processors_for(type, file_type)
187
+ bundled_processors = config[:bundle_processors][type]
188
+ if bundled_processors.any?
189
+ bundled_processors
190
+ else
191
+ self_processors_for(type, file_type)
192
+ end
193
+ end
194
+
195
+ def self_processors_for(type, file_type)
196
+ processors = []
197
+
198
+ processors.concat config[:postprocessors][type]
199
+ if type != file_type && processor = config[:transformers][file_type][type]
200
+ processors << processor
201
+ end
202
+ processors.concat config[:preprocessors][file_type]
203
+
204
+ if processors.any? || mime_type_charset_detecter(type)
205
+ processors << FileReader
206
+ end
207
+
208
+ processors
209
+ end
210
+
211
+ private
212
+ def register_config_processor(type, mime_type, processor = nil, &block)
213
+ processor ||= block
214
+
215
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
216
+ processors.unshift(processor)
217
+ processors
218
+ end
219
+ end
220
+
221
+ def unregister_config_processor(type, mime_type, processor)
222
+ self.config = hash_reassoc(config, type, mime_type) do |processors|
223
+ processors.delete_if { |p| p == processor || p.class == processor }
224
+ processors
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+
4
+ module Sprockets
5
+ # Functional utilities for dealing with Processor functions.
6
+ #
7
+ # A Processor is a general function that may modify or transform an asset as
8
+ # part of the pipeline. CoffeeScript to JavaScript conversion, Minification
9
+ # or Concatenation are all implemented as seperate Processor steps.
10
+ #
11
+ # Processors maybe any object that responds to call. So procs or a class that
12
+ # defines a self.call method.
13
+ #
14
+ # For ergonomics, processors may return a number of shorthand values.
15
+ # Unfortunately, this means that processors can not compose via ordinary
16
+ # function composition. The composition helpers here can help.
17
+ module ProcessorUtils
18
+ extend self
19
+
20
+ class CompositeProcessor < Struct.new(:processor_strategy, :param, :processors) # :nodoc:
21
+ SINGULAR = lambda { |param, input| ProcessorUtils.call_processor param, input }
22
+ PLURAL = lambda { |param, input| ProcessorUtils.call_processors param, input }
23
+
24
+ def self.create(processors)
25
+ if processors.length == 1
26
+ new SINGULAR, processors.first, processors
27
+ else
28
+ new PLURAL, processors, processors
29
+ end
30
+ end
31
+
32
+ def call(input)
33
+ processor_strategy.call param, input
34
+ end
35
+
36
+ def cache_key
37
+ ProcessorUtils.processors_cache_keys(processors)
38
+ end
39
+ end
40
+
41
+ # Public: Compose processors in right to left order.
42
+ #
43
+ # processors - Array of processors callables
44
+ #
45
+ # Returns a composed Proc.
46
+ def compose_processors(*processors)
47
+ CompositeProcessor.create processors
48
+ end
49
+
50
+ # Public: Invoke list of processors in right to left order.
51
+ #
52
+ # The right to left order processing mirrors standard function composition.
53
+ # Think about:
54
+ #
55
+ # bundle.call(uglify.call(coffee.call(input)))
56
+ #
57
+ # processors - Array of processor callables
58
+ # input - Hash of input data to pass to each processor
59
+ #
60
+ # Returns a Hash with :data and other processor metadata key/values.
61
+ def call_processors(processors, input)
62
+ data = input[:data] || ""
63
+ metadata = (input[:metadata] || {}).dup
64
+
65
+ processors.reverse_each do |processor|
66
+ result = call_processor(processor, input.merge(data: data, metadata: metadata))
67
+ data = result.delete(:data)
68
+ metadata.merge!(result)
69
+ end
70
+
71
+ metadata.merge(data: data)
72
+ end
73
+
74
+ # Public: Invoke processor.
75
+ #
76
+ # processor - Processor callables
77
+ # input - Hash of input data to pass to processor
78
+ #
79
+ # Returns a Hash with :data and other processor metadata key/values.
80
+ def call_processor(processor, input)
81
+ metadata = (input[:metadata] || {}).dup
82
+ metadata[:data] = input[:data]
83
+
84
+ case result = processor.call({data: "", metadata: {}}.merge(input))
85
+ when NilClass
86
+ metadata
87
+ when Hash
88
+ metadata.merge(result)
89
+ when String
90
+ metadata.merge(data: result)
91
+ else
92
+ raise TypeError, "invalid processor return type: #{result.class}"
93
+ end
94
+ end
95
+
96
+ # Internal: Get processor defined cached key.
97
+ #
98
+ # processor - Processor function
99
+ #
100
+ # Returns JSON serializable key or nil.
101
+ def processor_cache_key(processor)
102
+ processor.cache_key if processor.respond_to?(:cache_key)
103
+ end
104
+
105
+ # Internal: Get combined cache keys for set of processors.
106
+ #
107
+ # processors - Array of processor functions
108
+ #
109
+ # Returns Array of JSON serializable keys.
110
+ def processors_cache_keys(processors)
111
+ processors.map { |processor| processor_cache_key(processor) }
112
+ end
113
+
114
+ # Internal: Set of all "simple" value types allowed to be returned in
115
+ # processor metadata.
116
+ VALID_METADATA_VALUE_TYPES = Set.new([
117
+ String,
118
+ Symbol,
119
+ TrueClass,
120
+ FalseClass,
121
+ NilClass
122
+ ] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
123
+
124
+ # Internal: Set of all nested compound metadata types that can nest values.
125
+ VALID_METADATA_COMPOUND_TYPES = Set.new([
126
+ Array,
127
+ Hash,
128
+ Set
129
+ ]).freeze
130
+
131
+ # Internal: Hash of all "simple" value types allowed to be returned in
132
+ # processor metadata.
133
+ VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash|
134
+ hash[type] = true
135
+ end.freeze
136
+
137
+ # Internal: Hash of all nested compound metadata types that can nest values.
138
+ VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash|
139
+ hash[type] = true
140
+ end.freeze
141
+
142
+ # Internal: Set of all allowed metadata types.
143
+ VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze
144
+
145
+ # Internal: Validate returned result of calling a processor pipeline and
146
+ # raise a friendly user error message.
147
+ #
148
+ # result - Metadata Hash returned from call_processors
149
+ #
150
+ # Returns result or raises a TypeError.
151
+ def validate_processor_result!(result)
152
+ if !result.instance_of?(Hash)
153
+ raise TypeError, "processor metadata result was expected to be a Hash, but was #{result.class}"
154
+ end
155
+
156
+ if !result[:data].instance_of?(String)
157
+ raise TypeError, "processor :data was expected to be a String, but as #{result[:data].class}"
158
+ end
159
+
160
+ result.each do |key, value|
161
+ if !key.instance_of?(Symbol)
162
+ raise TypeError, "processor metadata[#{key.inspect}] expected to be a Symbol"
163
+ end
164
+ end
165
+
166
+ result
167
+ end
168
+ end
169
+ end