condenser 1.3 → 1.5
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.
- checksums.yaml +4 -4
- data/lib/condenser/asset.rb +103 -25
- data/lib/condenser/build_cache.rb +23 -8
- data/lib/condenser/cache/file_store.rb +1 -0
- data/lib/condenser/cache/memory_store.rb +1 -0
- data/lib/condenser/cache/null_store.rb +1 -0
- data/lib/condenser/cache_store.rb +1 -0
- data/lib/condenser/context.rb +1 -0
- data/lib/condenser/encoding_utils.rb +2 -0
- data/lib/condenser/environment.rb +2 -0
- data/lib/condenser/errors.rb +2 -0
- data/lib/condenser/export.rb +11 -6
- data/lib/condenser/helpers/parse_helpers.rb +23 -1
- data/lib/condenser/manifest.rb +3 -1
- data/lib/condenser/minifiers/sass_minifier.rb +2 -0
- data/lib/condenser/minifiers/terser_minifier.rb +2 -0
- data/lib/condenser/minifiers/uglify_minifier.rb +2 -0
- data/lib/condenser/pipeline.rb +2 -0
- data/lib/condenser/processors/babel_processor.rb +19 -16
- data/lib/condenser/processors/css_media_combiner_processor.rb +7 -5
- data/lib/condenser/processors/js_analyzer.rb +149 -42
- data/lib/condenser/processors/node_processor.rb +3 -0
- data/lib/condenser/processors/purgecss_processor.rb +2 -0
- data/lib/condenser/processors/rollup_processor.rb +289 -136
- data/lib/condenser/resolve.rb +41 -10
- data/lib/condenser/server.rb +22 -20
- data/lib/condenser/templating_engine/ejs.rb +2 -0
- data/lib/condenser/templating_engine/erb.rb +2 -0
- data/lib/condenser/transformers/dart_sass_transformer.rb +5 -3
- data/lib/condenser/transformers/jst_transformer.rb +2 -0
- data/lib/condenser/transformers/sass/functions.rb +2 -0
- data/lib/condenser/transformers/sass/importer.rb +2 -0
- data/lib/condenser/transformers/sass.rb +2 -0
- data/lib/condenser/transformers/sass_transformer.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/base.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/tag.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/template.rb +3 -1
- data/lib/condenser/transformers/svg_transformer/template_error.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/value.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/var_generator.rb +2 -0
- data/lib/condenser/transformers/svg_transformer.rb +2 -0
- data/lib/condenser/utils.rb +2 -0
- data/lib/condenser/version.rb +3 -1
- data/lib/condenser/writers/brotli_writer.rb +2 -0
- data/lib/condenser/writers/file_writer.rb +2 -0
- data/lib/condenser/writers/zlib_writer.rb +2 -0
- data/lib/condenser.rb +2 -0
- data/lib/rake/condensertask.rb +2 -0
- data/test/cache_test.rb +115 -20
- data/test/dependency_test.rb +51 -2
- data/test/manifest_test.rb +17 -2
- data/test/postprocessors/css_media_combiner_test.rb +9 -12
- data/test/preprocessor/babel_test.rb +876 -349
- data/test/preprocessor/js_analyzer_test.rb +208 -4
- data/test/processors/rollup/dynamic_import_test.rb +358 -0
- data/test/processors/rollup_test.rb +37 -56
- data/test/resolve_test.rb +14 -9
- data/test/server_test.rb +10 -9
- data/test/test_helper.rb +6 -3
- data/test/transformers/dart_scss_test.rb +2 -2
- data/test/transformers/scss_test.rb +2 -2
- metadata +6 -11
- 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,8 +16,10 @@ class Condenser::JSAnalyzer
|
|
14
16
|
@sourcefile = input[:source_file]
|
15
17
|
@source = input[:source]
|
16
18
|
@stack = [:main]
|
19
|
+
@previous = [[]]
|
17
20
|
|
18
|
-
input[:
|
21
|
+
input[:linked_assets] ||= Set.new
|
22
|
+
input[:export_dependencies] ||= Set.new
|
19
23
|
|
20
24
|
scan_until(/\A(\/\/[^\n]*(\n|\z))*/)
|
21
25
|
if matched
|
@@ -28,6 +32,7 @@ class Condenser::JSAnalyzer
|
|
28
32
|
end
|
29
33
|
|
30
34
|
last_postion = nil
|
35
|
+
last_stack = nil
|
31
36
|
while !eos?
|
32
37
|
case @stack.last
|
33
38
|
|
@@ -35,14 +40,19 @@ class Condenser::JSAnalyzer
|
|
35
40
|
scan_until(/(\$\{|\`)/)
|
36
41
|
case matched
|
37
42
|
when '`'
|
38
|
-
@stack.pop
|
43
|
+
@stack.pop if pre_match[-1] != "\\" && pre_match[-1] != "\\"
|
39
44
|
when '${'
|
40
45
|
@stack << :tick_statment
|
41
46
|
end
|
42
47
|
|
43
48
|
when :import
|
44
|
-
scan_until(/[\"\'
|
45
|
-
|
49
|
+
scan_until(/[\"\'\`\(]/)
|
50
|
+
dynamic = if matched == "("
|
51
|
+
scan_until(/[\"\'\`]/)
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
filename = case matched
|
46
56
|
when "\""
|
47
57
|
double_quoted_value
|
48
58
|
when "'"
|
@@ -50,66 +60,142 @@ class Condenser::JSAnalyzer
|
|
50
60
|
when '`'
|
51
61
|
tick_quoted_value
|
52
62
|
end
|
53
|
-
|
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|\))/)
|
54
72
|
@stack.pop
|
55
73
|
|
74
|
+
when :export
|
75
|
+
input[:type] = "module"
|
76
|
+
input[:exports] = true;
|
77
|
+
input[:default_export] = true if gobble(/\s+default/)
|
78
|
+
gobble(/\s+/)
|
79
|
+
|
80
|
+
if gobble(/\{/)
|
81
|
+
@stack << :brackets
|
82
|
+
@previous << []
|
83
|
+
elsif gobble(/\*/)
|
84
|
+
@stack << :export_from
|
85
|
+
else
|
86
|
+
@stack.pop
|
87
|
+
end
|
88
|
+
|
89
|
+
when :export_from
|
90
|
+
if gobble(/\s+from\s+/)
|
91
|
+
scan_until(/\"|\'/)
|
92
|
+
input[:export_dependencies] << case matched
|
93
|
+
when '"'
|
94
|
+
double_quoted_value
|
95
|
+
when "'"
|
96
|
+
single_quoted_value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
@stack.pop
|
100
|
+
@stack.pop
|
101
|
+
|
56
102
|
else
|
57
|
-
scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export|import|\z)/)
|
103
|
+
scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export(?![[:alnum:]])|import(?![[:alnum:]])|\z)/)
|
58
104
|
|
59
105
|
case matched
|
60
106
|
when '//'
|
61
107
|
scan_until(/(\n|\z)/)
|
108
|
+
@previous.last << :single_line_comment
|
62
109
|
when '/*'
|
63
110
|
scan_until(/\*\//)
|
111
|
+
@previous.last << :multi_line_comment
|
64
112
|
when '"'
|
65
113
|
double_quoted_value
|
114
|
+
@previous.last << :double_quoted_value
|
66
115
|
when "'"
|
67
116
|
single_quoted_value
|
117
|
+
@previous.last << :single_quoted_value
|
68
118
|
when '`'
|
69
119
|
@stack << :tick_value
|
70
120
|
when '/'
|
71
|
-
if
|
72
|
-
|
73
|
-
|
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?
|
74
145
|
regex_value
|
75
146
|
end
|
76
147
|
else
|
77
148
|
regex_value
|
78
149
|
end
|
79
150
|
when '('
|
80
|
-
@stack
|
151
|
+
@stack << if pre_match =~ /\W*(for|while)\s*\z/
|
152
|
+
:loop
|
153
|
+
else
|
154
|
+
:parenthesis
|
155
|
+
end
|
81
156
|
when ')'
|
82
|
-
raise unexptected_token(")") if @stack.last != :parenthesis
|
83
|
-
@stack.pop
|
157
|
+
raise unexptected_token(")") if @stack.last != :parenthesis && @stack.last != :loop
|
158
|
+
@previous.last << @stack.pop
|
84
159
|
when '{'
|
85
160
|
@stack.push :brackets
|
161
|
+
@previous << []
|
86
162
|
when '}'
|
87
163
|
case @stack.last
|
88
|
-
when :
|
164
|
+
when :tick_statment
|
89
165
|
@stack.pop
|
166
|
+
when :brackets
|
167
|
+
@stack.pop
|
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
|
90
174
|
else
|
91
175
|
raise unexptected_token("}")
|
92
176
|
end
|
93
177
|
when 'export'
|
94
178
|
if @stack.last == :main
|
95
|
-
|
96
|
-
input[:default_export] = true if next_word == 'default'
|
179
|
+
@stack << :export
|
97
180
|
end
|
98
181
|
when 'import'
|
99
|
-
|
100
|
-
@stack << :import
|
101
|
-
end
|
182
|
+
@stack << :import
|
102
183
|
else
|
103
184
|
@stack.pop
|
104
185
|
end
|
105
186
|
end
|
106
187
|
|
107
|
-
if last_postion == @index
|
188
|
+
if last_postion == @index && last_stack == @stack.last
|
189
|
+
syntax_error = Condenser::SyntaxError.new("Error parsing JS file with JSAnalyzer")
|
190
|
+
syntax_error.instance_variable_set(:@path, @sourcefile)
|
108
191
|
raise Condenser::SyntaxError, "Error parsing JS file with JSAnalyzer"
|
109
192
|
else
|
110
193
|
last_postion = @index
|
194
|
+
last_stack = @stack.last
|
111
195
|
end
|
112
196
|
end
|
197
|
+
|
198
|
+
raise Condenser::SyntaxError, "Unexpected EOF" if !@stack.empty? && @stack.last != :main
|
113
199
|
end
|
114
200
|
|
115
201
|
def unexptected_token(token)
|
@@ -119,26 +205,26 @@ class Condenser::JSAnalyzer
|
|
119
205
|
|
120
206
|
message = "Unexpected token #{token} #{@sourcefile} #{lineno.to_s.rjust(4)}:#{(@index-start)}"
|
121
207
|
message << "\n#{lineno.to_s.rjust(4)}: " << @source[start..uptop]
|
122
|
-
message << "\n #{'-'* (@index-1-start)}#{'^'*(@matched.length)}"
|
208
|
+
message << "\n #{'-'* ([@index-1-start,1].max)}#{'^'*([@matched.length,1].max)}"
|
123
209
|
message << "\n"
|
124
|
-
|
210
|
+
|
211
|
+
syntax_error = Condenser::SyntaxError.new(message)
|
212
|
+
syntax_error.instance_variable_set(:@path, @sourcefile)
|
213
|
+
syntax_error
|
125
214
|
end
|
126
215
|
|
127
216
|
def double_quoted_value
|
128
|
-
ret_value =
|
217
|
+
ret_value = String.new
|
129
218
|
|
130
|
-
while scan_until(/[\"\n]/)
|
131
|
-
|
219
|
+
while scan_until(/[\"\n\\]/)
|
220
|
+
case matched
|
221
|
+
when "\n"
|
132
222
|
raise unexptected_token("\\n")
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
ret_value << pre_match << "\\\""
|
139
|
-
end
|
140
|
-
|
141
|
-
|
223
|
+
when "\\"
|
224
|
+
ret_value << pre_match << matched << gobble(1)
|
225
|
+
when "\""
|
226
|
+
ret_value << pre_match
|
227
|
+
return ret_value
|
142
228
|
else
|
143
229
|
ret_value << match
|
144
230
|
end
|
@@ -146,7 +232,7 @@ class Condenser::JSAnalyzer
|
|
146
232
|
end
|
147
233
|
|
148
234
|
def single_quoted_value
|
149
|
-
ret_value =
|
235
|
+
ret_value = String.new
|
150
236
|
|
151
237
|
while scan_until(/[\'\n]/)
|
152
238
|
if matched == "\n"
|
@@ -161,7 +247,7 @@ class Condenser::JSAnalyzer
|
|
161
247
|
end
|
162
248
|
|
163
249
|
def tick_quoted_value
|
164
|
-
ret_value =
|
250
|
+
ret_value = String.new
|
165
251
|
|
166
252
|
while scan_until(/[\`]/)
|
167
253
|
if matched == "\`" && pre_match[-1] != "\\"
|
@@ -174,16 +260,37 @@ class Condenser::JSAnalyzer
|
|
174
260
|
end
|
175
261
|
|
176
262
|
def regex_value
|
177
|
-
ret_value =
|
263
|
+
ret_value = String.new
|
178
264
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
185
290
|
end
|
186
291
|
end
|
292
|
+
|
293
|
+
ret_value
|
187
294
|
end
|
188
295
|
|
189
296
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'tempfile'
|
2
4
|
require 'open3'
|
3
5
|
|
@@ -60,6 +62,7 @@ class Condenser
|
|
60
62
|
lineno = lines[0][/\((\d+):\d+\)$/, 1] if lines[0]
|
61
63
|
lineno ||= 1
|
62
64
|
error.set_backtrace(["#{source_file}:#{lineno}"] + caller)
|
65
|
+
error.instance_variable_set(:@path, source_file)
|
63
66
|
error
|
64
67
|
end
|
65
68
|
|