sprockets 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
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