sprockets 4.0.0.beta4 → 4.0.0.beta5

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