condenser 1.4 → 1.5.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/lib/condenser/asset.rb +55 -8
  3. data/lib/condenser/build_cache.rb +2 -0
  4. data/lib/condenser/cache/file_store.rb +1 -0
  5. data/lib/condenser/cache/memory_store.rb +1 -0
  6. data/lib/condenser/cache/null_store.rb +1 -0
  7. data/lib/condenser/cache_store.rb +1 -0
  8. data/lib/condenser/context.rb +1 -0
  9. data/lib/condenser/encoding_utils.rb +2 -0
  10. data/lib/condenser/environment.rb +2 -0
  11. data/lib/condenser/errors.rb +2 -0
  12. data/lib/condenser/export.rb +11 -6
  13. data/lib/condenser/helpers/parse_helpers.rb +19 -4
  14. data/lib/condenser/manifest.rb +6 -4
  15. data/lib/condenser/minifiers/sass_minifier.rb +2 -0
  16. data/lib/condenser/minifiers/terser_minifier.rb +2 -0
  17. data/lib/condenser/minifiers/uglify_minifier.rb +2 -0
  18. data/lib/condenser/pipeline.rb +2 -0
  19. data/lib/condenser/processors/babel_processor.rb +11 -2
  20. data/lib/condenser/processors/css_media_combiner_processor.rb +7 -5
  21. data/lib/condenser/processors/js_analyzer.rb +109 -37
  22. data/lib/condenser/processors/node_processor.rb +2 -0
  23. data/lib/condenser/processors/purgecss_processor.rb +2 -0
  24. data/lib/condenser/processors/rollup_processor.rb +289 -136
  25. data/lib/condenser/resolve.rb +15 -7
  26. data/lib/condenser/server.rb +22 -20
  27. data/lib/condenser/templating_engine/ejs.rb +2 -0
  28. data/lib/condenser/templating_engine/erb.rb +2 -0
  29. data/lib/condenser/transformers/dart_sass_transformer.rb +5 -3
  30. data/lib/condenser/transformers/jst_transformer.rb +2 -0
  31. data/lib/condenser/transformers/sass/functions.rb +2 -0
  32. data/lib/condenser/transformers/sass/importer.rb +2 -0
  33. data/lib/condenser/transformers/sass.rb +2 -0
  34. data/lib/condenser/transformers/sass_transformer.rb +2 -0
  35. data/lib/condenser/transformers/svg_transformer/base.rb +2 -0
  36. data/lib/condenser/transformers/svg_transformer/tag.rb +2 -0
  37. data/lib/condenser/transformers/svg_transformer/template.rb +3 -1
  38. data/lib/condenser/transformers/svg_transformer/template_error.rb +2 -0
  39. data/lib/condenser/transformers/svg_transformer/value.rb +2 -0
  40. data/lib/condenser/transformers/svg_transformer/var_generator.rb +2 -0
  41. data/lib/condenser/transformers/svg_transformer.rb +2 -0
  42. data/lib/condenser/utils.rb +2 -0
  43. data/lib/condenser/version.rb +3 -1
  44. data/lib/condenser/writers/brotli_writer.rb +2 -0
  45. data/lib/condenser/writers/file_writer.rb +2 -0
  46. data/lib/condenser/writers/zlib_writer.rb +2 -0
  47. data/lib/condenser.rb +2 -0
  48. data/lib/rake/condensertask.rb +2 -0
  49. data/test/cache_test.rb +14 -14
  50. data/test/manifest_test.rb +17 -2
  51. data/test/postprocessors/css_media_combiner_test.rb +9 -12
  52. data/test/preprocessor/babel_test.rb +843 -327
  53. data/test/preprocessor/js_analyzer_test.rb +174 -5
  54. data/test/processors/rollup/dynamic_import_test.rb +358 -0
  55. data/test/processors/rollup_test.rb +37 -56
  56. data/test/resolve_test.rb +4 -9
  57. data/test/server_test.rb +6 -5
  58. data/test/transformers/dart_scss_test.rb +2 -2
  59. data/test/transformers/scss_test.rb +2 -2
  60. metadata +6 -11
  61. data/lib/condenser/minifiers/package-lock.json +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 171baa1e5cd8016c4b2e7f142147d174410085ede075c101da43b743a4ad6fec
4
- data.tar.gz: 9c97b58fb2bf540b335f6504997da99fac72891f61a0a0631639ae3fd9f28482
3
+ metadata.gz: 6f9c3ee00b220c07b9a0791215a5f260ac84c4d7432c19a0764339ebae02d48d
4
+ data.tar.gz: 460361e7976a33f448a06d348582aa70c370cd7fcc334b4839e47c1c6738b2ef
5
5
  SHA512:
6
- metadata.gz: decc8b993d0e99c5212f3c0b1d4be88de43d49ab1386f32936a0dae482fc9b45de55cbbc51d99eafce58bcd68809a20fd27267ef0fe62d3aa3a244e33e68992e
7
- data.tar.gz: e39a90c28c7beab789885e53e35ae0ad43340ff4009f81ad1586ac16eb0073dbaf999b69a94f78e796f5bfe5e93a16e0a45481e76b706d0e496dfbe5435b4966
6
+ metadata.gz: 7af0b344077380ed7c25ad7a7c27e16a11f584518688f4f30e18e4a84af252c9aaa889aca549108ca7487bce6d246985ee15e34b5864f88e146578a34a328e04
7
+ data.tar.gz: 3f425933f7f0c2f07b9e5da5d17a78a0234bf35bd0c0e1095822a1c62f62b74cd93a935b8717a3f249d3321733826530c1afa82008e39fd847e61d93a35f2c13
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
  require 'digest/md5'
3
5
  require 'digest/sha1'
@@ -10,7 +12,7 @@ class Condenser
10
12
  include EncodingUtils
11
13
 
12
14
  attr_reader :environment, :filename, :content_types, :source_file, :source_path
13
- attr_reader :linked_assets, :content_types_digest, :exports
15
+ attr_reader :content_types_digest, :exports, :type
14
16
  attr_writer :source, :sourcemap
15
17
 
16
18
  attr_accessor :imports, :processed
@@ -65,7 +67,9 @@ class Condenser
65
67
  def process_dependencies
66
68
  deps = @environment.cache.fetch "direct-deps/#{cache_key}" do
67
69
  process
68
- @process_dependencies.map { |fn| [normalize_filename_base(fn[0]), fn[1]] }
70
+ # Sort so etag and cache key are same irrelevant of ordering of
71
+ # dependencies
72
+ @process_dependencies.map { |fn| [normalize_filename_base(fn[0]), fn[1]] }.sort_by { |d| d[0] }
69
73
  end
70
74
 
71
75
  deps.inject([]) do |memo, i|
@@ -80,7 +84,24 @@ class Condenser
80
84
  def export_dependencies
81
85
  deps = @environment.cache.fetch "export-deps/#{cache_key}" do
82
86
  process
83
- (@export_dependencies + @process_dependencies).map { |fn| [normalize_filename_base(fn[0]), fn[1]] }
87
+ # Sort so etag and cache key are same irrelevant of ordering of
88
+ # dependencies
89
+ (@export_dependencies + @process_dependencies).map { |fn| [normalize_filename_base(fn[0]), fn[1]] }.sort_by { |d| d[0] }
90
+ end
91
+
92
+ deps.inject([]) do |memo, i|
93
+ i[0] = File.join(@environment.base, i[0].delete_prefix('!')) if i[0].start_with?('!') && @environment.base
94
+ @environment.resolve(i[0], File.dirname(@source_file), accept: i[1], npm: true).each do |asset|
95
+ memo << asset
96
+ end
97
+ memo
98
+ end
99
+ end
100
+
101
+ def linked_assets
102
+ deps = @environment.cache.fetch "linked-assets/#{cache_key}" do
103
+ process
104
+ @linked_assets.map { |fn| [normalize_filename_base(fn[0]), fn[1]] }
84
105
  end
85
106
 
86
107
  deps.inject([]) do |memo, i|
@@ -122,7 +143,7 @@ class Condenser
122
143
  end
123
144
 
124
145
  def all_process_dependencies(visited = Set.new)
125
- f = []
146
+ f = Set.new
126
147
  if !visited.include?(@source_file)
127
148
  f << @source_file
128
149
  visited << self.source_file
@@ -135,7 +156,7 @@ class Condenser
135
156
  end
136
157
 
137
158
  def all_export_dependencies(visited = Set.new)
138
- f = []
159
+ f = Set.new
139
160
  if !visited.include?(@source_file)
140
161
  f << @source_file
141
162
  visited << self.source_file
@@ -192,7 +213,7 @@ class Condenser
192
213
  ]
193
214
  end
194
215
 
195
- @ecv = Digest::SHA1.base64digest(JSON.generate(f))
216
+ @ecv = Digest::SHA1.hexdigest(JSON.generate(f))
196
217
  end
197
218
 
198
219
  def needs_reprocessing!
@@ -296,6 +317,7 @@ class Condenser
296
317
  data[:digest_name] = @environment.digestor.name.sub(/^.*::/, '').downcase
297
318
  data[:process_dependencies] = normialize_dependency_names(data[:process_dependencies])
298
319
  data[:export_dependencies] = normialize_dependency_names(data[:export_dependencies])
320
+ data[:linked_assets] = normialize_dependency_names(data[:linked_assets])
299
321
 
300
322
  # Do this here and at the end so cache_key can be calculated if we
301
323
  # run this block
@@ -313,6 +335,15 @@ class Condenser
313
335
  @processors = data[:processors]
314
336
  @processors_loaded = true
315
337
  @processed = true
338
+ @type = data[:type]
339
+
340
+ digestor = @environment.digestor.new
341
+ digestor << data[:source]
342
+ all_dependenies(export_dependencies, Set.new, :export_dependencies) do |dep|
343
+ digestor << dep.source
344
+ end
345
+ data[:etag] = digestor.digest.unpack('H*'.freeze).first
346
+ @etag = data[:etag]
316
347
  data
317
348
  end
318
349
  end
@@ -329,6 +360,8 @@ class Condenser
329
360
  @default_export = result[:default_export]
330
361
  @exports = result[:exports]
331
362
  @processors = result[:processors]
363
+ @etag = result[:etag]
364
+ @type = result[:type]
332
365
  load_processors
333
366
 
334
367
  @processed = true
@@ -353,6 +386,9 @@ class Condenser
353
386
  process
354
387
  dirname, basename, extensions, mime_types = @environment.decompose_path(@filename)
355
388
  data = {
389
+ etag: @etag,
390
+ type: @type,
391
+
356
392
  source: @source.dup,
357
393
  source_file: @source_file,
358
394
 
@@ -429,7 +465,11 @@ class Condenser
429
465
  process
430
466
  @digest.unpack('H*'.freeze).first
431
467
  end
432
- alias_method :etag, :hexdigest
468
+
469
+ def etag
470
+ process
471
+ @etag
472
+ end
433
473
 
434
474
  def integrity
435
475
  process
@@ -437,7 +477,14 @@ class Condenser
437
477
  end
438
478
 
439
479
  def to_json
440
- { path: path, digest: hexdigest, size: size, integrity: integrity }
480
+ {
481
+ path: path,
482
+ etag: etag,
483
+ type: type,
484
+ size: size,
485
+ digest: hexdigest,
486
+ integrity: integrity
487
+ }
441
488
  end
442
489
 
443
490
  def write(output_directory)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser
2
4
  class BuildCache
3
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Condenser::Cache
3
4
  class FileStore < Condenser::CacheStore
4
5
  GITKEEP_FILES = ['.gitkeep', '.keep'].freeze
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Condenser::Cache
3
4
  # Public: Basic in memory LRU cache.
4
5
  #
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Condenser::Cache
3
4
  class NullStore
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Condenser::CacheStore
3
4
 
4
5
  def fetch(key)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'set'
3
4
  require 'condenser/errors'
4
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser
2
4
  module EncodingUtils
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/sha2'
2
4
  require 'condenser/context'
3
5
  require 'condenser/cache_store'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Basic Condenser error classes
2
4
  class Condenser
3
5
  class Error < StandardError; end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser
2
4
  class Export
3
5
 
4
- attr_reader :filename, :source, :sourcemap, :content_types, :digest, :digest_name
6
+ attr_reader :filename, :source, :sourcemap, :content_types, :digest, :digest_name, :etag, :type
5
7
 
6
8
  def initialize(env, input={})
7
9
  @environment = env
@@ -12,6 +14,8 @@ class Condenser
12
14
  @content_types = input[:content_types]
13
15
  @digest = input[:digest]
14
16
  @digest_name = input[:digest_name]
17
+ @etag = input[:etag]
18
+ @type = input[:type]
15
19
  end
16
20
 
17
21
  def path
@@ -43,7 +47,6 @@ class Condenser
43
47
  def hexdigest
44
48
  @digest.unpack('H*'.freeze).first
45
49
  end
46
- alias_method :etag, :hexdigest
47
50
 
48
51
  def integrity
49
52
  "#{@digest_name}-#{[@digest].pack('m0')}"
@@ -51,10 +54,12 @@ class Condenser
51
54
 
52
55
  def to_json
53
56
  {
54
- 'path' => path,
55
- 'size' => size,
56
- 'digest' => hexdigest,
57
- 'integrity' => integrity
57
+ path: path,
58
+ etag: etag,
59
+ type: type,
60
+ size: size,
61
+ digest: hexdigest,
62
+ integrity: integrity
58
63
  }
59
64
  end
60
65
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Condenser::ParseHelpers
2
4
 
3
5
  attr_accessor :matched
4
6
 
5
7
  def eos?
6
- @index >= (@source.size - 1)
8
+ @index >= @source.size
7
9
  end
8
10
 
9
11
  def scan_until(r)
@@ -56,9 +58,22 @@ module Condenser::ParseHelpers
56
58
  end
57
59
 
58
60
  def gobble(r)
59
- m = @source.match(r, @index)
60
- if m&.begin(0) == @index
61
- scan_until(r)
61
+ if r.is_a?(Regexp)
62
+ m = @source.match(r, @index)
63
+ if m&.begin(0) == @index
64
+ scan_until(r)
65
+ end
66
+ else
67
+ forward(1)
68
+ @source[@index-1];
69
+ end
70
+ end
71
+
72
+ def peek(n=1)
73
+ if n.is_a?(Regexp)
74
+ @source.match(n, @index)
75
+ else
76
+ @source.slice(@index, n)
62
77
  end
63
78
  end
64
79
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser
2
4
  class Manifest
3
5
 
@@ -39,7 +41,7 @@ class Condenser
39
41
 
40
42
  if File.exist?(@filename)
41
43
  begin
42
- @data = JSON.parse(File.read(@filename))
44
+ @data = JSON.parse(File.read(@filename), symbolize_names: true).transform_keys(&:to_s)
43
45
  rescue JSON::ParserError => e
44
46
  @data = {}
45
47
  logger.error "#{@filename} is invalid: #{e.class} #{e.message}"
@@ -72,14 +74,14 @@ class Condenser
72
74
  @data[asset.filename] = export.to_json
73
75
  outputs = export.write(@dir)
74
76
  asset.linked_assets.each do |la|
75
- @environment.resolve(la).each { |a| outputs += add_asset(a) }
77
+ outputs += add_asset(la)
76
78
  end
77
79
  outputs
78
80
  end
79
81
 
80
82
  def [](key)
81
83
  add(key) if @environment
82
- @data[key]
84
+ @data[key.delete_prefix('/')]
83
85
  end
84
86
 
85
87
  def compile(*args)
@@ -104,7 +106,7 @@ class Condenser
104
106
  # Cleanup old assets in the compile directory. By default it will keep the
105
107
  # latest version and remove any other files over 4 weeks old.
106
108
  def clean(age = 2419200)
107
- clean_dir(@dir, @data.values.map{ |v| v['path'] }, Time.now - age)
109
+ clean_dir(@dir, @data.values.map{ |v| v[:path] }, Time.now - age)
108
110
  end
109
111
 
110
112
  def clean_dir(dir, assets, age)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser::SassMinifier
2
4
 
3
5
  def self.instance
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser::TerserMinifier < Condenser::NodeProcessor
2
4
 
3
5
  def initialize(dir, options = {})
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser::UglifyMinifier < Condenser::NodeProcessor
2
4
 
3
5
  class Error < StandardError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser
2
4
  module Pipeline
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  class Condenser::BabelProcessor < Condenser::NodeProcessor
@@ -16,6 +18,7 @@ class Condenser::BabelProcessor < Condenser::NodeProcessor
16
18
  targets: { browsers: '> 1% and not dead' }
17
19
  }]
18
20
  ]
21
+ options[:highlightCode] = false if !options.has_key?(:highlightCode)
19
22
 
20
23
  packages = options.slice(:plugins, :presets).values.reduce(&:+).map { |p| p.is_a?(Array) ? p[0] : p}
21
24
  packages.unshift('@babel/core')
@@ -80,11 +83,17 @@ class Condenser::BabelProcessor < Condenser::NodeProcessor
80
83
  end
81
84
  end
82
85
 
83
-
86
+ opts['preset']&.each do |preset|
87
+ preset[0] = preset[0].gsub(/"@?babel[\/-][^"]+"/) { |m| "require(#{m})"}
88
+ end
89
+ opts['plugins']&.each do |preset|
90
+ preset[0] = preset[0].gsub(/"@?babel[\/-][^"]+"/) { |m| "require(#{m})"}
91
+ end
92
+
84
93
  result = exec_runtime(<<-JS)
85
94
  const babel = require("#{File.join(npm_module_path('@babel/core'))}");
86
95
  const source = #{JSON.generate(input[:source])};
87
- const options = #{JSON.generate(opts).gsub(/"@?babel[\/-][^"]+"/) { |m| "require(#{m})"}};
96
+ const options = #{JSON.generate(opts)};
88
97
 
89
98
  let imports = [];
90
99
  let defaultExport = false;
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser::CSSMediaCombinerProcessor
2
4
 
3
5
  include Condenser::ParseHelpers
@@ -10,7 +12,7 @@ class Condenser::CSSMediaCombinerProcessor
10
12
  end
11
13
 
12
14
  def reduce_media_query(queries)
13
- output = ''
15
+ output = String.new
14
16
  queries.each do |query, contents|
15
17
  output << query if query
16
18
  output << if contents.is_a?(Hash)
@@ -30,12 +32,12 @@ class Condenser::CSSMediaCombinerProcessor
30
32
  @selectors = []
31
33
  @media_queries = {}
32
34
 
33
- input[:source] = ''
35
+ input[:source] = String.new
34
36
  while !eos?
35
37
  output = if @selectors.empty?
36
38
  input[:source]
37
39
  else
38
- (@selectors[0...-1].reduce(@media_queries) { |hash, selector| hash[selector] ||= {} }[@selectors.last] ||= '')
40
+ (@selectors[0...-1].reduce(@media_queries) { |hash, selector| hash[selector] ||= {} }[@selectors.last] ||= String.new)
39
41
  end
40
42
 
41
43
  case @stack.last
@@ -64,11 +66,11 @@ class Condenser::CSSMediaCombinerProcessor
64
66
  @stack.pop
65
67
  end
66
68
  else
67
- case scan_until(/(@media[^\{]*{|\Z)/)
69
+ case scan_until(/(@media[^\{]*{|\z)/)
68
70
  when ''
69
71
  output << pre_match
70
72
  else
71
- output << pre_match
73
+ output << pre_match.rstrip
72
74
  @selectors << matched.squish
73
75
  @stack << :media_query
74
76
  end