sprockets 4.0.0.beta4 → 4.0.0.beta5

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.
@@ -140,10 +140,6 @@ module Sprockets
140
140
 
141
141
  # Read into memory and process if theres a processor pipeline
142
142
  if processors.any?
143
- source_uri, _ = resolve!(unloaded.filename, pipeline: :source)
144
- source_asset = load(source_uri)
145
-
146
- source_path = source_asset.digest_path
147
143
 
148
144
  result = call_processors(processors, {
149
145
  environment: self,
@@ -151,12 +147,10 @@ module Sprockets
151
147
  uri: unloaded.uri,
152
148
  filename: unloaded.filename,
153
149
  load_path: load_path,
154
- source_path: source_path,
155
150
  name: name,
156
151
  content_type: type,
157
152
  metadata: {
158
- dependencies: dependencies,
159
- map: []
153
+ dependencies: dependencies
160
154
  }
161
155
  })
162
156
  validate_processor_result!(result)
@@ -120,11 +120,14 @@ module Sprockets
120
120
  return to_enum(__method__, *args) unless block_given?
121
121
 
122
122
  environment = self.environment.cached
123
- args.flatten.each do |path|
124
- environment.find_all_linked_assets(path) do |asset|
125
- yield asset
123
+ promises = args.flatten.map do |path|
124
+ Concurrent::Promise.execute(executor: executor) do
125
+ environment.find_all_linked_assets(path) do |asset|
126
+ yield asset
127
+ end
126
128
  end
127
129
  end
130
+ promises.each(&:wait!)
128
131
 
129
132
  nil
130
133
  end
@@ -160,7 +163,6 @@ module Sprockets
160
163
 
161
164
  filenames = []
162
165
  concurrent_exporters = []
163
- executor = Concurrent::FixedThreadPool.new(Concurrent.processor_count)
164
166
 
165
167
  find(*args) do |asset|
166
168
  mtime = Time.now.iso8601
@@ -183,11 +185,6 @@ module Sprockets
183
185
  exporters_for_asset(asset) do |exporter|
184
186
  next if exporter.skip?(logger)
185
187
 
186
- if !environment.export_concurrent
187
- exporter.call
188
- next
189
- end
190
-
191
188
  if promise.nil?
192
189
  promise = Concurrent::Promise.new(executor: executor) { exporter.call }
193
190
  concurrent_exporters << promise.execute
@@ -265,6 +262,8 @@ module Sprockets
265
262
  def clobber
266
263
  FileUtils.rm_r(directory) if File.exist?(directory)
267
264
  logger.info "Removed #{directory}"
265
+ # if we have an environment clear the cache too
266
+ environment.cache.clear if environment
268
267
  nil
269
268
  end
270
269
 
@@ -294,6 +293,7 @@ module Sprockets
294
293
  exporters = [Exporters::FileExporter]
295
294
 
296
295
  environment.exporters.each do |mime_type, exporter_list|
296
+ next unless asset.content_type
297
297
  next unless environment.match_mime_type? asset.content_type, mime_type
298
298
  exporter_list.each do |exporter|
299
299
  exporters << exporter
@@ -324,5 +324,9 @@ module Sprockets
324
324
  logger
325
325
  end
326
326
  end
327
+
328
+ def executor
329
+ @executor ||= environment.export_concurrent ? :fast : :immediate
330
+ end
327
331
  end
328
332
  end
@@ -7,7 +7,6 @@ module Sprockets
7
7
  extend self
8
8
 
9
9
  MANIFEST_RE = /^\.sprockets-manifest-[0-9a-f]{32}.json$/
10
- LEGACY_MANIFEST_RE = /^manifest(-[0-9a-f]{32})?.json$/
11
10
 
12
11
  # Public: Generate a new random manifest path.
13
12
  #
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Sprockets
5
+ module Npm
6
+ # Internal: Override resolve_alternates to install package.json behavior.
7
+ #
8
+ # load_path - String environment path
9
+ # logical_path - String path relative to base
10
+ #
11
+ # Returns candiate filenames.
12
+ def resolve_alternates(load_path, logical_path)
13
+ candidates, deps = super
14
+
15
+ dirname = File.join(load_path, logical_path)
16
+
17
+ if directory?(dirname)
18
+ filename = File.join(dirname, 'package.json')
19
+
20
+ if self.file?(filename)
21
+ deps << build_file_digest_uri(filename)
22
+ read_package_directives(dirname, filename) do |path|
23
+ if file?(path)
24
+ candidates << path
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ return candidates, deps
31
+ end
32
+
33
+ # Internal: Read package.json's main and style directives.
34
+ #
35
+ # dirname - String path to component directory.
36
+ # filename - String path to package.json.
37
+ #
38
+ # Returns nothing.
39
+ def read_package_directives(dirname, filename)
40
+ package = JSON.parse(File.read(filename), create_additions: false)
41
+
42
+ case package['main']
43
+ when String
44
+ yield File.expand_path(package['main'], dirname)
45
+ when nil
46
+ yield File.expand_path('index.js', dirname)
47
+ end
48
+
49
+ yield File.expand_path(package['style'], dirname) if package['style']
50
+ end
51
+ end
52
+ end
@@ -81,7 +81,7 @@ module Sprockets
81
81
  end
82
82
  else
83
83
  def absolute_path?(path)
84
- path[0] == File::SEPARATOR
84
+ path.start_with?(File::SEPARATOR)
85
85
  end
86
86
  end
87
87
 
@@ -128,6 +128,30 @@ module Sprockets
128
128
  (Pathname.new(base) + path).to_s
129
129
  end
130
130
 
131
+ # Public: Sets pipeline for path
132
+ #
133
+ # path - String path
134
+ # extensions - List of file extensions
135
+ # pipeline - Pipeline
136
+ #
137
+ # Examples
138
+ #
139
+ # set_pipeline('path/file.js.erb', config[:mime_exts], config[:pipeline_exts], :source)
140
+ # # => 'path/file.source.js.erb'
141
+ #
142
+ # set_pipeline('path/some.file.source.js.erb', config[:mime_exts], config[:pipeline_exts], :debug)
143
+ # # => 'path/some.file.debug.js.erb'
144
+ #
145
+ # Returns string path with pipeline parsed in
146
+ def set_pipeline(path, mime_exts, pipeline_exts, pipeline)
147
+ extension, _ = match_path_extname(path, mime_exts)
148
+ path.chomp!(extension)
149
+ pipeline_old, _ = match_path_extname(path, pipeline_exts)
150
+ path.chomp!(pipeline_old)
151
+
152
+ "#{path}.#{pipeline}#{extension}"
153
+ end
154
+
131
155
  # Internal: Get relative path for root path and subpath.
132
156
  #
133
157
  # path - String path
@@ -9,17 +9,37 @@ module Sprockets
9
9
  # available.
10
10
  class DefaultSourceMap
11
11
  def call(input)
12
- result = { data: input[:data] }
13
- map = input[:metadata][:map]
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.count
17
+ basename = File.basename(filename)
18
+ mime_exts = input[:environment].config[:mime_exts]
19
+ pipeline_exts = input[:environment].config[:pipeline_exts]
14
20
  if map.nil? || map.empty?
15
- result[:map] ||= []
16
- input[:data].each_line.with_index do |_, index|
17
- line = index + 1
18
- result[:map] << { source: input[:source_path], generated: [line , 0], original: [line, 0] }
19
- end
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
+ }
20
28
  end
21
29
  return result
22
30
  end
31
+
32
+ private
33
+
34
+ def default_mappings(lines)
35
+ if (lines == 0)
36
+ ""
37
+ elsif (lines == 1)
38
+ "AAAA"
39
+ else
40
+ "AAAA;" + "AACA;"*(lines - 2) + "AACA"
41
+ end
42
+ end
23
43
  end
24
44
  end
25
45
  end
@@ -49,15 +49,13 @@ module Sprockets
49
49
  def call(input)
50
50
  css, map = Autoload::Sass::Engine.new(
51
51
  input[:data],
52
- @options.merge(filename: 'filename')
52
+ @options.merge(filename: input[:filename])
53
53
  ).render_with_sourcemap('')
54
54
 
55
55
  css = css.sub("/*# sourceMappingURL= */\n", '')
56
56
 
57
- map = SourceMapUtils.combine_source_maps(
58
- input[:metadata][:map],
59
- SourceMapUtils.decode_json_source_map(map.to_json(css_uri: 'uri'))["mappings"]
60
- )
57
+ map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input)
58
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
61
59
 
62
60
  { data: css, map: map }
63
61
  end
@@ -80,13 +80,8 @@ module Sprockets
80
80
 
81
81
  css = css.sub("\n/*# sourceMappingURL= */\n", '')
82
82
 
83
- map = SourceMapUtils.combine_source_maps(
84
- input[:metadata][:map],
85
- expand_map_sources(
86
- SourceMapUtils.decode_json_source_map(map.to_json(css_uri: '', type: :inline))["mappings"],
87
- input[:environment]
88
- )
89
- )
83
+ map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input)
84
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
90
85
 
91
86
  # Track all imported files
92
87
  sass_dependencies = Set.new([input[:filename]])
@@ -100,17 +95,6 @@ module Sprockets
100
95
 
101
96
  private
102
97
 
103
- def expand_source(source, env)
104
- uri, _ = env.resolve!(source, pipeline: :source)
105
- env.load(uri).digest_path
106
- end
107
-
108
- def expand_map_sources(mapping, env)
109
- mapping.each do |map|
110
- map[:source] = expand_source(map[:source], env)
111
- end
112
- end
113
-
114
98
  # Public: Build the cache store to be used by the Sass engine.
115
99
  #
116
100
  # input - the input hash.
@@ -9,23 +9,24 @@ module Sprockets
9
9
  @options = {
10
10
  syntax: :scss,
11
11
  style: :compressed,
12
- source_map_embed: true,
13
- source_map_file: '.'
12
+ source_map_contents: false,
13
+ omit_source_map_url: true,
14
14
  }.merge(options).freeze
15
15
  end
16
16
 
17
17
  def call(input)
18
18
  # SassC requires the template to be modifiable
19
19
  input_data = input[:data].frozen? ? input[:data].dup : input[:data]
20
- data = Autoload::SassC::Engine.new(input_data, @options.merge(filename: 'filename')).render
20
+ engine = Autoload::SassC::Engine.new(input_data, @options.merge(filename: input[:filename], source_map_file: "#{input[:filename]}.map"))
21
+
22
+ css = engine.render.sub(/^\n^\/\*# sourceMappingURL=.*\*\/$/m, '')
21
23
 
22
- match_data = data.match(/(.*)\n\/\*# sourceMappingURL=data:application\/json;base64,(.+) \*\//m)
23
- css, map = match_data[1], Base64.decode64(match_data[2])
24
-
25
- map = SourceMapUtils.combine_source_maps(
26
- input[:metadata][:map],
27
- SourceMapUtils.decode_json_source_map(map)["mappings"]
28
- )
24
+ begin
25
+ map = SourceMapUtils.format_source_map(JSON.parse(engine.source_map), input)
26
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
27
+ rescue SassC::NotRenderedError
28
+ map = input[:metadata][:map]
29
+ end
29
30
 
30
31
  { data: css, map: map }
31
32
  end
@@ -27,25 +27,18 @@ module Sprockets
27
27
  engine.render.sub(/^\n^\/\*# sourceMappingURL=.*\*\/$/m, '')
28
28
  end
29
29
 
30
- map = SourceMapUtils.decode_json_source_map(engine.source_map)
31
- sources = map['sources'].map do |s|
32
- expand_source(PathUtils.join(File.dirname(input[:filename]), s), input[:environment])
33
- end
34
-
35
- map = map["mappings"].each do |m|
36
- m[:source] = PathUtils.join(File.dirname(input[:filename]), m[:source])
37
- end
38
-
39
- map = SourceMapUtils.combine_source_maps(
40
- input[:metadata][:map],
41
- expand_map_sources(map, input[:environment])
42
- )
30
+ begin
31
+ map = SourceMapUtils.format_source_map(JSON.parse(engine.source_map), input)
32
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
43
33
 
44
- engine.dependencies.each do |dependency|
45
- context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.filename)
34
+ engine.dependencies.each do |dependency|
35
+ context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.filename)
36
+ end
37
+ rescue SassC::NotRenderedError
38
+ map = input[:metadata][:map]
46
39
  end
47
40
 
48
- context.metadata.merge(data: css, map: map, sources: sources)
41
+ context.metadata.merge(data: css, map: map)
49
42
  end
50
43
 
51
44
  private
@@ -56,7 +49,7 @@ module Sprockets
56
49
  syntax: self.class.syntax,
57
50
  load_paths: input[:environment].paths,
58
51
  importer: @importer_class,
59
- source_map_contents: true,
52
+ source_map_contents: false,
60
53
  source_map_file: "#{input[:filename]}.map",
61
54
  omit_source_map_url: true,
62
55
  sprockets: {
@@ -241,7 +241,7 @@ module Sprockets
241
241
  # If the request url contains a fingerprint, set a long
242
242
  # expires on the response
243
243
  if path_fingerprint(env["PATH_INFO"])
244
- headers["Cache-Control"] << ", max-age=31536000"
244
+ headers["Cache-Control"] << ", max-age=31536000, immutable"
245
245
 
246
246
  # Otherwise set `must-revalidate` since the asset could be modified.
247
247
  else
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ require 'sprockets/uri_utils'
3
+ require 'sprockets/path_utils'
4
+
2
5
  module Sprockets
3
6
  class SourceMapCommentProcessor
4
7
  def self.call(input)
@@ -21,7 +24,9 @@ module Sprockets
21
24
  uri, _ = env.resolve!(input[:filename], accept: map_type)
22
25
  map = env.load(uri)
23
26
 
24
- path = PathUtils.relative_path_from(input[:filename], map.full_digest_path)
27
+ uri, params = URIUtils.parse_asset_uri(input[:uri])
28
+ uri = env.expand_from_root(params[:index_alias]) if params[:index_alias]
29
+ path = PathUtils.relative_path_from(PathUtils.split_subpath(input[:load_path], uri), map.digest_path)
25
30
 
26
31
  asset.metadata.merge(
27
32
  data: asset.source + (comment % path),
@@ -3,24 +3,25 @@ require 'set'
3
3
 
4
4
  module Sprockets
5
5
  class SourceMapProcessor
6
- def self.call(input)
7
- case input[:content_type]
6
+ def self.original_content_type(source_map_content_type, error_when_not_found: true)
7
+ case source_map_content_type
8
8
  when "application/js-sourcemap+json"
9
9
  accept = "application/javascript"
10
10
  when "application/css-sourcemap+json"
11
11
  accept = "text/css"
12
12
  else
13
- fail input[:content_type]
13
+ fail(source_map_content_type) if error_when_not_found
14
+ source_map_content_type
14
15
  end
16
+ end
15
17
 
18
+ def self.call(input)
16
19
  links = Set.new(input[:metadata][:links])
17
-
18
20
  env = input[:environment]
19
21
 
20
- uri, _ = env.resolve!(input[:filename], accept: accept)
21
- asset = env.load(uri)
22
- map = asset.metadata[:map] || []
23
- sources = asset.metadata[:sources]
22
+ uri, _ = env.resolve!(input[:filename], accept: original_content_type(input[:content_type]))
23
+ asset = env.load(uri)
24
+ map = asset.metadata[:map]
24
25
 
25
26
  # TODO: Because of the default piplene hack we have to apply dependencies
26
27
  # from compiled asset to the source map, otherwise the source map cache
@@ -28,19 +29,16 @@ module Sprockets
28
29
  dependencies = Set.new(input[:metadata][:dependencies])
29
30
  dependencies.merge(asset.metadata[:dependencies])
30
31
 
31
- map.map { |m| m[:source] }.uniq.compact.each do |source|
32
- # TODO: Resolve should expect fingerprints
33
- fingerprint = source[/-([0-9a-f]{7,128})\.[^.]+\z/, 1]
34
- if fingerprint
35
- path = source.sub("-#{fingerprint}", "")
36
- else
37
- path = source
38
- end
39
- uri, _ = env.resolve!(path)
32
+ map["file"] = PathUtils.split_subpath(input[:load_path], input[:filename])
33
+ sources = map["sections"] ? map["sections"].map { |s| s["map"]["sources"] }.flatten : map["sources"]
34
+
35
+ sources.each do |source|
36
+ source = PathUtils.join(File.dirname(map["file"]), source)
37
+ uri, _ = env.resolve!(source)
40
38
  links << uri
41
39
  end
42
40
 
43
- json = env.encode_json_source_map(map, sources: sources, filename: asset.logical_path)
41
+ json = JSON.generate(map)
44
42
 
45
43
  { data: json, links: links, dependencies: dependencies }
46
44
  end