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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Condenser::JSAnalyzer
2
4
 
3
5
  include Condenser::ParseHelpers
@@ -14,7 +16,9 @@ class Condenser::JSAnalyzer
14
16
  @sourcefile = input[:source_file]
15
17
  @source = input[:source]
16
18
  @stack = [:main]
19
+ @previous = [[]]
17
20
 
21
+ input[:linked_assets] ||= Set.new
18
22
  input[:export_dependencies] ||= Set.new
19
23
 
20
24
  scan_until(/\A(\/\/[^\n]*(\n|\z))*/)
@@ -36,14 +40,19 @@ class Condenser::JSAnalyzer
36
40
  scan_until(/(\$\{|\`)/)
37
41
  case matched
38
42
  when '`'
39
- @stack.pop
43
+ @stack.pop if pre_match[-1] != "\\" && pre_match[-1] != "\\"
40
44
  when '${'
41
45
  @stack << :tick_statment
42
46
  end
43
47
 
44
48
  when :import
45
- scan_until(/[\"\'\`]/)
46
- input[:export_dependencies] << case matched
49
+ scan_until(/[\"\'\`\(]/)
50
+ dynamic = if matched == "("
51
+ scan_until(/[\"\'\`]/)
52
+ true
53
+ end
54
+
55
+ filename = case matched
47
56
  when "\""
48
57
  double_quoted_value
49
58
  when "'"
@@ -51,16 +60,26 @@ class Condenser::JSAnalyzer
51
60
  when '`'
52
61
  tick_quoted_value
53
62
  end
54
- scan_until(/(;|\n)/)
63
+
64
+ input[:type] = "module"
65
+ if dynamic
66
+ input[:process_dependencies] << filename
67
+ input[:linked_assets] << filename
68
+ else
69
+ input[:export_dependencies] << filename
70
+ end
71
+ scan_until(/(;|\n|\))/)
55
72
  @stack.pop
56
73
 
57
74
  when :export
75
+ input[:type] = "module"
58
76
  input[:exports] = true;
59
77
  input[:default_export] = true if gobble(/\s+default/)
60
78
  gobble(/\s+/)
61
79
 
62
80
  if gobble(/\{/)
63
81
  @stack << :brackets
82
+ @previous << []
64
83
  elsif gobble(/\*/)
65
84
  @stack << :export_from
66
85
  else
@@ -81,42 +100,77 @@ class Condenser::JSAnalyzer
81
100
  @stack.pop
82
101
 
83
102
  else
84
- scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export|import|\z)/)
103
+ scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export(?![[:alnum:]])|import(?![[:alnum:]])|\z)/)
85
104
 
86
105
  case matched
87
106
  when '//'
88
107
  scan_until(/(\n|\z)/)
108
+ @previous.last << :single_line_comment
89
109
  when '/*'
90
110
  scan_until(/\*\//)
111
+ @previous.last << :multi_line_comment
91
112
  when '"'
92
113
  double_quoted_value
114
+ @previous.last << :double_quoted_value
93
115
  when "'"
94
116
  single_quoted_value
117
+ @previous.last << :single_quoted_value
95
118
  when '`'
96
119
  @stack << :tick_value
97
120
  when '/'
98
- if match_index = @source.rindex(/(\w+|\)|\])\s*\//, @index)
99
- match = @source.match(/(\w+|\)|\])\s*\//, match_index)
100
- if match[0].length + match_index != @index
121
+ if pre_match.match(/(\W|\A)(void|typeof|return|export)\z/)
122
+ regex_value
123
+ elsif match_index = @source.rindex(/(\w+|\)|\])(\s*)\//m, @index)
124
+ match = @source.match(/(\w+|\)|\])(\s*)\//, match_index)
125
+
126
+ x = @previous.last.dup
127
+ while match[2]&.index("\n") && [:single_line_comment, :multi_line_comment].include?(x.last)
128
+ case x.last
129
+ when :single_line_comment
130
+ match_index = @source.rindex(/[^\n]*\/\/[^\n]*\s*/m, match.begin(2))
131
+ match = @source.match(/(\w+|\)|\])\s*\//, match_index)
132
+ when :multi_line_comment
133
+ match_index = @source.rindex(/\/\*/m, match.begin(2))
134
+ match = @source.match(/(\w+|\)|\])\s*\//, match_index)
135
+ end
136
+ end
137
+
138
+ if @previous.last.last == :loop && match[1] =~ /\)\z/
139
+ regex_value
140
+ elsif %w(void typeof return export).include?(match[1])
141
+ regex_value
142
+ elsif match[0].length + match_index != @index
143
+ regex_value
144
+ elsif match[1].strip.empty?
101
145
  regex_value
102
146
  end
103
147
  else
104
148
  regex_value
105
149
  end
106
150
  when '('
107
- @stack.push :parenthesis
151
+ @stack << if pre_match =~ /\W*(for|while)\s*\z/
152
+ :loop
153
+ else
154
+ :parenthesis
155
+ end
108
156
  when ')'
109
- raise unexptected_token(")") if @stack.last != :parenthesis
110
- @stack.pop
157
+ raise unexptected_token(")") if @stack.last != :parenthesis && @stack.last != :loop
158
+ @previous.last << @stack.pop
111
159
  when '{'
112
160
  @stack.push :brackets
161
+ @previous << []
113
162
  when '}'
114
163
  case @stack.last
115
164
  when :tick_statment
116
165
  @stack.pop
117
166
  when :brackets
118
167
  @stack.pop
119
- @stack << :export_from if @stack.last == :export
168
+ @previous.pop
169
+ @previous.last << :brackets
170
+ if @stack.last == :export
171
+ @stack.pop
172
+ @stack << :export_from if peek(/\s+from/i)
173
+ end
120
174
  else
121
175
  raise unexptected_token("}")
122
176
  end
@@ -125,9 +179,7 @@ class Condenser::JSAnalyzer
125
179
  @stack << :export
126
180
  end
127
181
  when 'import'
128
- if @stack.last == :main
129
- @stack << :import
130
- end
182
+ @stack << :import
131
183
  else
132
184
  @stack.pop
133
185
  end
@@ -142,6 +194,8 @@ class Condenser::JSAnalyzer
142
194
  last_stack = @stack.last
143
195
  end
144
196
  end
197
+
198
+ raise Condenser::SyntaxError, "Unexpected EOF" if !@stack.empty? && @stack.last != :main
145
199
  end
146
200
 
147
201
  def unexptected_token(token)
@@ -151,7 +205,7 @@ class Condenser::JSAnalyzer
151
205
 
152
206
  message = "Unexpected token #{token} #{@sourcefile} #{lineno.to_s.rjust(4)}:#{(@index-start)}"
153
207
  message << "\n#{lineno.to_s.rjust(4)}: " << @source[start..uptop]
154
- message << "\n #{'-'* (@index-1-start)}#{'^'*(@matched.length)}"
208
+ message << "\n #{'-'* ([@index-1-start,1].max)}#{'^'*([@matched.length,1].max)}"
155
209
  message << "\n"
156
210
 
157
211
  syntax_error = Condenser::SyntaxError.new(message)
@@ -160,20 +214,17 @@ class Condenser::JSAnalyzer
160
214
  end
161
215
 
162
216
  def double_quoted_value
163
- ret_value = ""
217
+ ret_value = String.new
164
218
 
165
- while scan_until(/[\"\n]/)
166
- if matched == "\n"
219
+ while scan_until(/[\"\n\\]/)
220
+ case matched
221
+ when "\n"
167
222
  raise unexptected_token("\\n")
168
- elsif matched == "\""
169
- if pre_match[-1] != "\\"
170
- ret_value << pre_match
171
- return ret_value
172
- else
173
- ret_value << pre_match << "\\\""
174
- end
175
-
176
-
223
+ when "\\"
224
+ ret_value << pre_match << matched << gobble(1)
225
+ when "\""
226
+ ret_value << pre_match
227
+ return ret_value
177
228
  else
178
229
  ret_value << match
179
230
  end
@@ -181,7 +232,7 @@ class Condenser::JSAnalyzer
181
232
  end
182
233
 
183
234
  def single_quoted_value
184
- ret_value = ""
235
+ ret_value = String.new
185
236
 
186
237
  while scan_until(/[\'\n]/)
187
238
  if matched == "\n"
@@ -196,7 +247,7 @@ class Condenser::JSAnalyzer
196
247
  end
197
248
 
198
249
  def tick_quoted_value
199
- ret_value = ""
250
+ ret_value = String.new
200
251
 
201
252
  while scan_until(/[\`]/)
202
253
  if matched == "\`" && pre_match[-1] != "\\"
@@ -209,16 +260,37 @@ class Condenser::JSAnalyzer
209
260
  end
210
261
 
211
262
  def regex_value
212
- ret_value = ""
263
+ ret_value = String.new
213
264
 
214
- while scan_until(/\//)
215
- if matched == "/" && pre_match[-1] != "\\"
216
- ret_value << pre_match
217
- return ret_value
218
- else
219
- ret_value << pre_match
265
+ regex_stack = ['/']
266
+ while !regex_stack.empty?
267
+
268
+ scan_until(/[\/\[\]]/)
269
+ escaped = pre_match[-1] == "\\" && pre_match[-2] != "\\"
270
+ ret_value << pre_match
271
+ case matched
272
+ when "["
273
+ regex_stack << '[' if !escaped && regex_stack.last != "["
274
+ ret_value << matched
275
+ when "]"
276
+ regex_stack.pop if !escaped && regex_stack.last == "["
277
+ ret_value << matched
278
+ when "/"
279
+ # From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Character_class
280
+ #
281
+ # The lexical grammar does a very rough parse of regex literals, so
282
+ # that it does not end the regex literal at a / character which appears
283
+ # within a character class. This means /[/]/ is valid without needing
284
+ # to escape the /.
285
+ if !escaped && regex_stack.last != "[" && regex_stack.pop != "/"
286
+ raise unexptected_token("/")
287
+ else
288
+ ret_value << matched
289
+ end
220
290
  end
221
291
  end
292
+
293
+ ret_value
222
294
  end
223
295
 
224
296
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tempfile'
2
4
  require 'open3'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  class Condenser::PurgeCSSProcessor < Condenser::NodeProcessor