condenser 1.2 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/condenser/asset.rb +69 -35
- data/lib/condenser/build_cache.rb +22 -9
- data/lib/condenser/context.rb +9 -25
- data/lib/condenser/helpers/parse_helpers.rb +8 -1
- data/lib/condenser/manifest.rb +3 -1
- data/lib/condenser/pipeline.rb +8 -3
- data/lib/condenser/processors/babel_processor.rb +9 -15
- data/lib/condenser/processors/css_media_combiner_processor.rb +81 -0
- data/lib/condenser/processors/js_analyzer.rb +41 -8
- data/lib/condenser/processors/node_processor.rb +1 -0
- data/lib/condenser/processors/purgecss_processor.rb +6 -4
- data/lib/condenser/processors/rollup_processor.rb +38 -36
- data/lib/condenser/resolve.rb +27 -6
- data/lib/condenser/templating_engine/ejs.rb +1 -1
- data/lib/condenser/transformers/dart_sass_transformer.rb +285 -0
- data/lib/condenser/transformers/jst_transformer.rb +67 -17
- data/lib/condenser/transformers/sass/functions.rb +133 -0
- data/lib/condenser/transformers/sass/importer.rb +48 -0
- data/lib/condenser/transformers/sass.rb +4 -0
- data/lib/condenser/transformers/sass_transformer.rb +124 -281
- data/lib/condenser/transformers/svg_transformer/base.rb +26 -0
- data/lib/condenser/transformers/svg_transformer/tag.rb +54 -0
- data/lib/condenser/transformers/svg_transformer/template.rb +151 -0
- data/lib/condenser/transformers/svg_transformer/template_error.rb +2 -0
- data/lib/condenser/transformers/svg_transformer/value.rb +13 -0
- data/lib/condenser/transformers/svg_transformer/var_generator.rb +10 -0
- data/lib/condenser/transformers/svg_transformer.rb +19 -0
- data/lib/condenser/version.rb +1 -1
- data/lib/condenser.rb +17 -5
- data/test/cache_test.rb +157 -18
- data/test/dependency_test.rb +51 -2
- data/test/manifest_test.rb +34 -0
- data/test/minifiers/terser_minifier_test.rb +0 -1
- data/test/minifiers/uglify_minifier_test.rb +0 -1
- data/test/postprocessors/css_media_combiner_test.rb +107 -0
- data/test/postprocessors/purgecss_test.rb +62 -0
- data/test/preprocessor/babel_test.rb +703 -298
- data/test/preprocessor/js_analyzer_test.rb +35 -2
- data/test/processors/rollup_test.rb +50 -20
- data/test/resolve_test.rb +18 -9
- data/test/server_test.rb +15 -10
- data/test/templates/ejs_test.rb +2 -11
- data/test/templates/erb_test.rb +0 -5
- data/test/test_helper.rb +8 -3
- data/test/transformers/dart_scss_test.rb +139 -0
- data/test/transformers/jst_test.rb +165 -21
- data/test/transformers/scss_test.rb +14 -0
- data/test/transformers/svg_test.rb +40 -0
- metadata +23 -6
- data/lib/condenser/transformers/sass_transformer/importer.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 171baa1e5cd8016c4b2e7f142147d174410085ede075c101da43b743a4ad6fec
|
4
|
+
data.tar.gz: 9c97b58fb2bf540b335f6504997da99fac72891f61a0a0631639ae3fd9f28482
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: decc8b993d0e99c5212f3c0b1d4be88de43d49ab1386f32936a0dae482fc9b45de55cbbc51d99eafce58bcd68809a20fd27267ef0fe62d3aa3a244e33e68992e
|
7
|
+
data.tar.gz: e39a90c28c7beab789885e53e35ae0ad43340ff4009f81ad1586ac16eb0073dbaf999b69a94f78e796f5bfe5e93a16e0a45481e76b706d0e496dfbe5435b4966
|
data/lib/condenser/asset.rb
CHANGED
@@ -65,33 +65,31 @@ class Condenser
|
|
65
65
|
def process_dependencies
|
66
66
|
deps = @environment.cache.fetch "direct-deps/#{cache_key}" do
|
67
67
|
process
|
68
|
-
@process_dependencies
|
68
|
+
@process_dependencies.map { |fn| [normalize_filename_base(fn[0]), fn[1]] }
|
69
69
|
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
i = [i, @content_types] if i.is_a?(String)
|
71
|
+
deps.inject([]) do |memo, i|
|
72
|
+
i[0] = File.join(@environment.base, i[0].delete_prefix('!')) if i[0].start_with?('!') && @environment.base
|
74
73
|
@environment.resolve(i[0], File.dirname(@source_file), accept: i[1]).each do |asset|
|
75
|
-
|
74
|
+
memo << asset
|
76
75
|
end
|
76
|
+
memo
|
77
77
|
end
|
78
|
-
d
|
79
78
|
end
|
80
79
|
|
81
80
|
def export_dependencies
|
82
81
|
deps = @environment.cache.fetch "export-deps/#{cache_key}" do
|
83
82
|
process
|
84
|
-
@export_dependencies + @process_dependencies
|
83
|
+
(@export_dependencies + @process_dependencies).map { |fn| [normalize_filename_base(fn[0]), fn[1]] }
|
85
84
|
end
|
86
85
|
|
87
|
-
|
88
|
-
|
89
|
-
i = [i, @content_types] if i.is_a?(String)
|
86
|
+
deps.inject([]) do |memo, i|
|
87
|
+
i[0] = File.join(@environment.base, i[0].delete_prefix('!')) if i[0].start_with?('!') && @environment.base
|
90
88
|
@environment.resolve(i[0], File.dirname(@source_file), accept: i[1]).each do |asset|
|
91
|
-
|
89
|
+
memo << asset
|
92
90
|
end
|
91
|
+
memo
|
93
92
|
end
|
94
|
-
d
|
95
93
|
end
|
96
94
|
|
97
95
|
def has_default_export?
|
@@ -123,17 +121,27 @@ class Condenser
|
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
126
|
-
def all_process_dependencies
|
127
|
-
f = [
|
128
|
-
|
124
|
+
def all_process_dependencies(visited = Set.new)
|
125
|
+
f = []
|
126
|
+
if !visited.include?(@source_file)
|
127
|
+
f << @source_file
|
128
|
+
visited << self.source_file
|
129
|
+
end
|
130
|
+
|
131
|
+
all_dependenies(process_dependencies, visited, :process_dependencies) do |dep|
|
129
132
|
f << dep.source_file
|
130
133
|
end
|
131
134
|
f
|
132
135
|
end
|
133
136
|
|
134
|
-
def all_export_dependencies
|
135
|
-
f = [
|
136
|
-
|
137
|
+
def all_export_dependencies(visited = Set.new)
|
138
|
+
f = []
|
139
|
+
if !visited.include?(@source_file)
|
140
|
+
f << @source_file
|
141
|
+
visited << self.source_file
|
142
|
+
end
|
143
|
+
|
144
|
+
all_dependenies(export_dependencies, visited, :export_dependencies) do |dep|
|
137
145
|
f << dep.source_file
|
138
146
|
end
|
139
147
|
f
|
@@ -143,19 +151,29 @@ class Condenser
|
|
143
151
|
@cache_key ||= Digest::SHA1.base64digest(JSON.generate([
|
144
152
|
Condenser::VERSION,
|
145
153
|
@environment.pipline_digest,
|
146
|
-
@
|
154
|
+
normalize_filename_base(@source_file),
|
147
155
|
Digest::SHA256.file(@source_file).hexdigest,
|
148
156
|
@content_types_digest
|
149
157
|
]))
|
150
158
|
end
|
151
159
|
|
160
|
+
# Remove Enviroment base if it exists. This allows two of the same repos
|
161
|
+
# in a different location to use the same cache (like capistrano deploys)
|
162
|
+
def normalize_filename_base(source_filename)
|
163
|
+
if @environment.base && source_filename.start_with?(@environment.base)
|
164
|
+
'!'+source_filename.delete_prefix(@environment.base).delete_prefix(File::SEPARATOR)
|
165
|
+
else
|
166
|
+
source_filename
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
152
170
|
def process_cache_version
|
153
171
|
return @pcv if @pcv
|
154
172
|
|
155
173
|
f = []
|
156
|
-
all_dependenies(process_dependencies,
|
174
|
+
all_dependenies(process_dependencies, Set.new, :process_dependencies) do |dep|
|
157
175
|
f << [
|
158
|
-
|
176
|
+
normalize_filename_base(dep.source_file),
|
159
177
|
Digest::SHA256.file(dep.source_file).hexdigest
|
160
178
|
]
|
161
179
|
end
|
@@ -167,9 +185,9 @@ class Condenser
|
|
167
185
|
return @ecv if @ecv
|
168
186
|
|
169
187
|
f = []
|
170
|
-
all_dependenies(export_dependencies,
|
188
|
+
all_dependenies(export_dependencies, Set.new, :export_dependencies) do |dep|
|
171
189
|
f << [
|
172
|
-
|
190
|
+
normalize_filename_base(dep.source_file),
|
173
191
|
Digest::SHA256.file(dep.source_file).hexdigest
|
174
192
|
]
|
175
193
|
end
|
@@ -206,9 +224,9 @@ class Condenser
|
|
206
224
|
content_type: mime_types,
|
207
225
|
|
208
226
|
map: nil,
|
209
|
-
linked_assets:
|
210
|
-
process_dependencies:
|
211
|
-
export_dependencies:
|
227
|
+
linked_assets: Set.new,
|
228
|
+
process_dependencies: Set.new,
|
229
|
+
export_dependencies: Set.new,
|
212
230
|
|
213
231
|
processors: Set.new
|
214
232
|
}
|
@@ -276,6 +294,8 @@ class Condenser
|
|
276
294
|
|
277
295
|
data[:digest] = @environment.digestor.digest(data[:source])
|
278
296
|
data[:digest_name] = @environment.digestor.name.sub(/^.*::/, '').downcase
|
297
|
+
data[:process_dependencies] = normialize_dependency_names(data[:process_dependencies])
|
298
|
+
data[:export_dependencies] = normialize_dependency_names(data[:export_dependencies])
|
279
299
|
|
280
300
|
# Do this here and at the end so cache_key can be calculated if we
|
281
301
|
# run this block
|
@@ -285,15 +305,14 @@ class Condenser
|
|
285
305
|
@content_types = data[:content_type]
|
286
306
|
@digest = data[:digest]
|
287
307
|
@digest_name = data[:digest_name]
|
288
|
-
@linked_assets = data[:linked_assets]
|
289
|
-
@process_dependencies = data[:process_dependencies]
|
290
|
-
@export_dependencies = data[:export_dependencies]
|
308
|
+
@linked_assets = Set.new(data[:linked_assets])
|
309
|
+
@process_dependencies = Set.new(data[:process_dependencies])
|
310
|
+
@export_dependencies = Set.new(data[:export_dependencies])
|
291
311
|
@default_export = data[:default_export]
|
292
312
|
@exports = data[:exports]
|
293
313
|
@processors = data[:processors]
|
294
314
|
@processors_loaded = true
|
295
315
|
@processed = true
|
296
|
-
|
297
316
|
data
|
298
317
|
end
|
299
318
|
end
|
@@ -304,9 +323,9 @@ class Condenser
|
|
304
323
|
@content_types = result[:content_type]
|
305
324
|
@digest = result[:digest]
|
306
325
|
@digest_name = result[:digest_name]
|
307
|
-
@linked_assets = result[:linked_assets]
|
308
|
-
@process_dependencies = result[:process_dependencies]
|
309
|
-
@export_dependencies = result[:export_dependencies]
|
326
|
+
@linked_assets = Set.new(result[:linked_assets])
|
327
|
+
@process_dependencies = Set.new(result[:process_dependencies])
|
328
|
+
@export_dependencies = Set.new(result[:export_dependencies])
|
310
329
|
@default_export = result[:default_export]
|
311
330
|
@exports = result[:exports]
|
312
331
|
@processors = result[:processors]
|
@@ -315,6 +334,17 @@ class Condenser
|
|
315
334
|
@processed = true
|
316
335
|
end
|
317
336
|
|
337
|
+
def normialize_dependency_names(deps)
|
338
|
+
deps.map do |fn|
|
339
|
+
if fn.is_a?(String)
|
340
|
+
dirname, basename, extensions, mime_types = @environment.decompose_path(fn, source_file)
|
341
|
+
[dirname ? File.join(dirname, basename) : basename, mime_types.empty? ? @content_types : mime_types]
|
342
|
+
else
|
343
|
+
fn
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
318
348
|
def export
|
319
349
|
return @export if @export
|
320
350
|
|
@@ -335,11 +365,15 @@ class Condenser
|
|
335
365
|
export_dependencies: []
|
336
366
|
}
|
337
367
|
|
338
|
-
if
|
339
|
-
|
368
|
+
if @environment.exporters.has_key?(content_type)
|
369
|
+
@environment.exporters[content_type].each do |exporter|
|
370
|
+
@environment.logger.info { "Exporting #{self.filename} with #{exporter.name}" }
|
371
|
+
exporter.call(@environment, data)
|
372
|
+
end
|
340
373
|
end
|
341
374
|
|
342
375
|
if minifier = @environment.minifier_for(content_type)
|
376
|
+
@environment.logger.info { "Minifing #{self.filename} with #{minifier.name}" }
|
343
377
|
minifier.call(@environment, data)
|
344
378
|
end
|
345
379
|
|
@@ -22,6 +22,10 @@ class Condenser
|
|
22
22
|
else
|
23
23
|
@semaphore = Mutex.new
|
24
24
|
@listener = Listen.to(*path) do |modified, added, removed|
|
25
|
+
modified = Set.new(modified)
|
26
|
+
added = Set.new(added)
|
27
|
+
removed = Set.new(removed)
|
28
|
+
|
25
29
|
@semaphore.synchronize do
|
26
30
|
@logger.debug { "build cache semaphore locked by #{Thread.current.object_id}" }
|
27
31
|
@logger.debug do
|
@@ -33,7 +37,7 @@ class Condenser
|
|
33
37
|
end
|
34
38
|
|
35
39
|
globs = []
|
36
|
-
(added + removed).each do |file|
|
40
|
+
(added + removed + modified).each do |file|
|
37
41
|
globs << file.match(/([^\.]+)(\.|$)/).to_a[1]
|
38
42
|
if path_match = @path.find { |p| file.start_with?(p) }
|
39
43
|
a = file.delete_prefix(path_match).match(/([^\.]+)(\.|$)/).to_a[1]
|
@@ -122,16 +126,25 @@ class Condenser
|
|
122
126
|
|
123
127
|
def []=(value, assets)
|
124
128
|
@lookup_cache[value] = assets
|
129
|
+
|
130
|
+
if @fetching.nil?
|
131
|
+
begin
|
132
|
+
assets.each do |asset|
|
133
|
+
@fetching = Set.new
|
134
|
+
asset.all_process_dependencies(@fetching).each do |pd|
|
135
|
+
@process_dependencies[pd] ||= Set.new
|
136
|
+
@process_dependencies[pd] << asset
|
137
|
+
end
|
125
138
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
@process_dependencies[pd] << asset
|
130
|
-
end
|
139
|
+
@fetching = Set.new
|
140
|
+
asset.all_export_dependencies(@fetching).each do |pd|
|
141
|
+
@export_dependencies[pd] ||= Set.new
|
131
142
|
|
132
|
-
|
133
|
-
|
134
|
-
|
143
|
+
@export_dependencies[pd] << asset
|
144
|
+
end
|
145
|
+
end
|
146
|
+
ensure
|
147
|
+
@fetching = nil
|
135
148
|
end
|
136
149
|
end
|
137
150
|
end
|
data/lib/condenser/context.rb
CHANGED
@@ -35,7 +35,7 @@ class Condenser
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
attr_reader :environment, :filename
|
38
|
+
attr_reader :environment, :filename, :links, :dependencies
|
39
39
|
|
40
40
|
def initialize(environment)
|
41
41
|
@environment = environment
|
@@ -110,29 +110,14 @@ class Condenser
|
|
110
110
|
# `depend_on` allows you to state a dependency on a file without
|
111
111
|
# including it.
|
112
112
|
#
|
113
|
-
# This is used for caching purposes. Any changes made to
|
114
|
-
# the dependency file will invalidate the cache of the
|
115
|
-
# source file.
|
116
|
-
def depend_on(path)
|
117
|
-
if environment.absolute_path?(path) && environment.stat(path)
|
118
|
-
@dependencies << environment.build_file_digest_uri(path)
|
119
|
-
else
|
120
|
-
resolve(path)
|
121
|
-
end
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
|
125
|
-
# `depend_on_asset` allows you to state an asset dependency
|
126
|
-
# without including it.
|
127
|
-
#
|
128
113
|
# This is used for caching purposes. Any changes that would
|
129
114
|
# invalidate the dependency asset will invalidate the source
|
130
|
-
# file.
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
115
|
+
# file.
|
116
|
+
def depend_on(path)
|
117
|
+
d = environment.decompose_path(path)
|
118
|
+
@dependencies << [File.join(*d[0], d[1]), [d[3]]]
|
119
|
+
|
120
|
+
nil
|
136
121
|
end
|
137
122
|
|
138
123
|
# `depend_on_env` allows you to state a dependency on an environment
|
@@ -150,9 +135,8 @@ class Condenser
|
|
150
135
|
#
|
151
136
|
# Returns an Asset or nil.
|
152
137
|
def link_asset(path)
|
153
|
-
|
154
|
-
@links <<
|
155
|
-
asset
|
138
|
+
depend_on(path)
|
139
|
+
@links << path
|
156
140
|
end
|
157
141
|
|
158
142
|
# Returns a `data:` URI with the contents of the asset at the specified
|
@@ -3,7 +3,7 @@ module Condenser::ParseHelpers
|
|
3
3
|
attr_accessor :matched
|
4
4
|
|
5
5
|
def eos?
|
6
|
-
@index >= @source.size
|
6
|
+
@index >= (@source.size - 1)
|
7
7
|
end
|
8
8
|
|
9
9
|
def scan_until(r)
|
@@ -55,4 +55,11 @@ module Condenser::ParseHelpers
|
|
55
55
|
"#{lineno.to_s.rjust(4)}: " + @source[start..uptop] + "\n #{'-'* (@index-1-start)}#{'^'*(@matched.length)}"
|
56
56
|
end
|
57
57
|
|
58
|
+
def gobble(r)
|
59
|
+
m = @source.match(r, @index)
|
60
|
+
if m&.begin(0) == @index
|
61
|
+
scan_until(r)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
58
65
|
end
|
data/lib/condenser/manifest.rb
CHANGED
@@ -71,7 +71,9 @@ class Condenser
|
|
71
71
|
|
72
72
|
@data[asset.filename] = export.to_json
|
73
73
|
outputs = export.write(@dir)
|
74
|
-
asset.linked_assets.each
|
74
|
+
asset.linked_assets.each do |la|
|
75
|
+
@environment.resolve(la).each { |a| outputs += add_asset(a) }
|
76
|
+
end
|
75
77
|
outputs
|
76
78
|
end
|
77
79
|
|
data/lib/condenser/pipeline.rb
CHANGED
@@ -98,11 +98,16 @@ class Condenser
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def register_exporter(mime_type, engine)
|
101
|
-
@exporters[mime_type]
|
101
|
+
@exporters[mime_type] ||= []
|
102
|
+
@exporters[mime_type] << engine
|
102
103
|
end
|
103
104
|
|
104
|
-
def unregister_exporter(mime_type, engine)
|
105
|
-
|
105
|
+
def unregister_exporter(mime_type, engine=nil)
|
106
|
+
if engine.nil?
|
107
|
+
@exporters[mime_type].clear
|
108
|
+
else
|
109
|
+
@exporters[mime_type]&.reject! { |e| e == engine || e.is_a?(engine) }
|
110
|
+
end
|
106
111
|
end
|
107
112
|
|
108
113
|
def register_minifier(mime_type, engine)
|
@@ -4,7 +4,7 @@ class Condenser::BabelProcessor < Condenser::NodeProcessor
|
|
4
4
|
|
5
5
|
attr_accessor :options
|
6
6
|
|
7
|
-
def initialize(dir = nil, options
|
7
|
+
def initialize(dir = nil, **options)
|
8
8
|
super(dir)
|
9
9
|
|
10
10
|
options[:plugins] ||= [
|
@@ -95,23 +95,17 @@ class Condenser::BabelProcessor < Condenser::NodeProcessor
|
|
95
95
|
ImportDeclaration(path, state) {
|
96
96
|
imports.push(path.node.source.value);
|
97
97
|
},
|
98
|
-
|
98
|
+
|
99
|
+
ExportDeclaration(path, state) {
|
99
100
|
hasExports = true;
|
100
|
-
|
101
|
+
if (path.node.source) {
|
102
|
+
imports.push(path.node.source.value);
|
103
|
+
}
|
101
104
|
},
|
102
|
-
|
103
|
-
|
105
|
+
|
106
|
+
ExportDefaultDeclaration(path, state) {
|
104
107
|
defaultExport = true;
|
105
|
-
}
|
106
|
-
ExportAllDeclaration(path, state) {
|
107
|
-
hasExports = true;
|
108
|
-
},
|
109
|
-
ExportNamedDeclaration(path, state) {
|
110
|
-
hasExports = true;
|
111
|
-
},
|
112
|
-
ExportSpecifier(path, state) {
|
113
|
-
hasExports = true;
|
114
|
-
},
|
108
|
+
}
|
115
109
|
}
|
116
110
|
};
|
117
111
|
});
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class Condenser::CSSMediaCombinerProcessor
|
2
|
+
|
3
|
+
include Condenser::ParseHelpers
|
4
|
+
|
5
|
+
def self.setup(env)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.call(environment, input)
|
9
|
+
new.call(environment, input)
|
10
|
+
end
|
11
|
+
|
12
|
+
def reduce_media_query(queries)
|
13
|
+
output = ''
|
14
|
+
queries.each do |query, contents|
|
15
|
+
output << query if query
|
16
|
+
output << if contents.is_a?(Hash)
|
17
|
+
reduce_media_query(contents)
|
18
|
+
else
|
19
|
+
contents + '}'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
output
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(environment, input)
|
26
|
+
seek(0)
|
27
|
+
@sourcefile = input[:source_file]
|
28
|
+
@source = input[:source]
|
29
|
+
@stack = []
|
30
|
+
@selectors = []
|
31
|
+
@media_queries = {}
|
32
|
+
|
33
|
+
input[:source] = ''
|
34
|
+
while !eos?
|
35
|
+
output = if @selectors.empty?
|
36
|
+
input[:source]
|
37
|
+
else
|
38
|
+
(@selectors[0...-1].reduce(@media_queries) { |hash, selector| hash[selector] ||= {} }[@selectors.last] ||= '')
|
39
|
+
end
|
40
|
+
|
41
|
+
case @stack.last
|
42
|
+
when :media_query
|
43
|
+
scan_until(/(@media[^\{]*{|\{|\})/)
|
44
|
+
case matched
|
45
|
+
when '{'
|
46
|
+
output << pre_match << matched
|
47
|
+
@stack << :statement
|
48
|
+
when '}'
|
49
|
+
output << pre_match
|
50
|
+
@stack.pop
|
51
|
+
@selectors.pop
|
52
|
+
else
|
53
|
+
output << pre_match
|
54
|
+
@selectors << matched.squish
|
55
|
+
@stack << :media_query
|
56
|
+
end
|
57
|
+
when :statement
|
58
|
+
scan_until(/(\{|\})/)
|
59
|
+
output << pre_match << matched
|
60
|
+
case matched
|
61
|
+
when '{'
|
62
|
+
@stack << :statement
|
63
|
+
when '}'
|
64
|
+
@stack.pop
|
65
|
+
end
|
66
|
+
else
|
67
|
+
case scan_until(/(@media[^\{]*{|\Z)/)
|
68
|
+
when ''
|
69
|
+
output << pre_match
|
70
|
+
else
|
71
|
+
output << pre_match
|
72
|
+
@selectors << matched.squish
|
73
|
+
@stack << :media_query
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
input[:source] << reduce_media_query(@media_queries)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'condenser/helpers/parse_helpers'
|
2
|
-
|
3
1
|
class Condenser::JSAnalyzer
|
4
2
|
|
5
3
|
include Condenser::ParseHelpers
|
@@ -17,7 +15,7 @@ class Condenser::JSAnalyzer
|
|
17
15
|
@source = input[:source]
|
18
16
|
@stack = [:main]
|
19
17
|
|
20
|
-
input[:export_dependencies] ||=
|
18
|
+
input[:export_dependencies] ||= Set.new
|
21
19
|
|
22
20
|
scan_until(/\A(\/\/[^\n]*(\n|\z))*/)
|
23
21
|
if matched
|
@@ -30,6 +28,7 @@ class Condenser::JSAnalyzer
|
|
30
28
|
end
|
31
29
|
|
32
30
|
last_postion = nil
|
31
|
+
last_stack = nil
|
33
32
|
while !eos?
|
34
33
|
case @stack.last
|
35
34
|
|
@@ -55,6 +54,32 @@ class Condenser::JSAnalyzer
|
|
55
54
|
scan_until(/(;|\n)/)
|
56
55
|
@stack.pop
|
57
56
|
|
57
|
+
when :export
|
58
|
+
input[:exports] = true;
|
59
|
+
input[:default_export] = true if gobble(/\s+default/)
|
60
|
+
gobble(/\s+/)
|
61
|
+
|
62
|
+
if gobble(/\{/)
|
63
|
+
@stack << :brackets
|
64
|
+
elsif gobble(/\*/)
|
65
|
+
@stack << :export_from
|
66
|
+
else
|
67
|
+
@stack.pop
|
68
|
+
end
|
69
|
+
|
70
|
+
when :export_from
|
71
|
+
if gobble(/\s+from\s+/)
|
72
|
+
scan_until(/\"|\'/)
|
73
|
+
input[:export_dependencies] << case matched
|
74
|
+
when '"'
|
75
|
+
double_quoted_value
|
76
|
+
when "'"
|
77
|
+
single_quoted_value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@stack.pop
|
81
|
+
@stack.pop
|
82
|
+
|
58
83
|
else
|
59
84
|
scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export|import|\z)/)
|
60
85
|
|
@@ -87,15 +112,17 @@ class Condenser::JSAnalyzer
|
|
87
112
|
@stack.push :brackets
|
88
113
|
when '}'
|
89
114
|
case @stack.last
|
90
|
-
when :
|
115
|
+
when :tick_statment
|
116
|
+
@stack.pop
|
117
|
+
when :brackets
|
91
118
|
@stack.pop
|
119
|
+
@stack << :export_from if @stack.last == :export
|
92
120
|
else
|
93
121
|
raise unexptected_token("}")
|
94
122
|
end
|
95
123
|
when 'export'
|
96
124
|
if @stack.last == :main
|
97
|
-
|
98
|
-
input[:default_export] = true if next_word == 'default'
|
125
|
+
@stack << :export
|
99
126
|
end
|
100
127
|
when 'import'
|
101
128
|
if @stack.last == :main
|
@@ -106,10 +133,13 @@ class Condenser::JSAnalyzer
|
|
106
133
|
end
|
107
134
|
end
|
108
135
|
|
109
|
-
if last_postion == @index
|
136
|
+
if last_postion == @index && last_stack == @stack.last
|
137
|
+
syntax_error = Condenser::SyntaxError.new("Error parsing JS file with JSAnalyzer")
|
138
|
+
syntax_error.instance_variable_set(:@path, @sourcefile)
|
110
139
|
raise Condenser::SyntaxError, "Error parsing JS file with JSAnalyzer"
|
111
140
|
else
|
112
141
|
last_postion = @index
|
142
|
+
last_stack = @stack.last
|
113
143
|
end
|
114
144
|
end
|
115
145
|
end
|
@@ -123,7 +153,10 @@ class Condenser::JSAnalyzer
|
|
123
153
|
message << "\n#{lineno.to_s.rjust(4)}: " << @source[start..uptop]
|
124
154
|
message << "\n #{'-'* (@index-1-start)}#{'^'*(@matched.length)}"
|
125
155
|
message << "\n"
|
126
|
-
|
156
|
+
|
157
|
+
syntax_error = Condenser::SyntaxError.new(message)
|
158
|
+
syntax_error.instance_variable_set(:@path, @sourcefile)
|
159
|
+
syntax_error
|
127
160
|
end
|
128
161
|
|
129
162
|
def double_quoted_value
|
@@ -34,7 +34,7 @@ class Condenser::PurgeCSSProcessor < Condenser::NodeProcessor
|
|
34
34
|
const { PurgeCSS } = require("#{File.join(npm_module_path('purgecss'))}")
|
35
35
|
const options = #{@options.to_json}
|
36
36
|
options.css = [{
|
37
|
-
raw: #{input[:source]
|
37
|
+
raw: #{JSON.dump(input[:source])}
|
38
38
|
}]
|
39
39
|
if(options.safelist) {
|
40
40
|
options.safelist = options.safelist.map(s => {
|
@@ -44,13 +44,16 @@ class Condenser::PurgeCSSProcessor < Condenser::NodeProcessor
|
|
44
44
|
return s
|
45
45
|
})
|
46
46
|
}
|
47
|
+
if(!options.defaultExtractor) {
|
48
|
+
options.defaultExtractor = content => content.match(/[\\w\\-\\/\\:]+(?<!:)/g) || []
|
49
|
+
}
|
47
50
|
const result = new PurgeCSS().purge(options)
|
48
51
|
try {
|
49
52
|
result.then(
|
50
53
|
r => console.log(JSON.stringify({
|
51
54
|
success: r[0]
|
52
55
|
})),
|
53
|
-
function() {console.log(JSON.stringify({'error':
|
56
|
+
function(e) {console.log(JSON.stringify({'error': [e.name, e.message, e.stack]}))}
|
54
57
|
)
|
55
58
|
} catch(e) {
|
56
59
|
console.log(JSON.stringify({'error': [e.name, e.message, e.stack]}));
|
@@ -60,11 +63,10 @@ class Condenser::PurgeCSSProcessor < Condenser::NodeProcessor
|
|
60
63
|
if result['error'][0] == 'SyntaxError'
|
61
64
|
raise exec_syntax_error(result['error'][1], "/assets/#{input[:filename]}")
|
62
65
|
else
|
63
|
-
raise exec_runtime_error(result['error'][0] +
|
66
|
+
raise exec_runtime_error(result['error']["0"]["name"] + ": " + result['error']["0"]["reason"])
|
64
67
|
end
|
65
68
|
else
|
66
69
|
input[:source] = result["success"]["css"]
|
67
70
|
end
|
68
71
|
end
|
69
|
-
|
70
72
|
end
|