condenser 1.3 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52e335c087aab2b9bb3ef4540e54bfc5b8de0a888cab8f095c2f1dd778458bd4
4
- data.tar.gz: 566502264a1049f4ff5e33154caa56ef64ed74d14a8d72abd5e31628d59986c1
3
+ metadata.gz: 171baa1e5cd8016c4b2e7f142147d174410085ede075c101da43b743a4ad6fec
4
+ data.tar.gz: 9c97b58fb2bf540b335f6504997da99fac72891f61a0a0631639ae3fd9f28482
5
5
  SHA512:
6
- metadata.gz: 5c0fdf66cb6b17ff6a519af5205061f37e3af2f12896e45c2798154c46ba2db3dea57e219595b33e5433a82db17751bc8444453c5bc0371fb27013e97157352d
7
- data.tar.gz: 2486bd59e00c2d70ea683d2f69f1a71b0981b557144a08fd4965f9fe38a0c2da68d79e1c6fcba359718af3f9c26f4978619c3bb651f394cbb0db0172c450096f
6
+ metadata.gz: decc8b993d0e99c5212f3c0b1d4be88de43d49ab1386f32936a0dae482fc9b45de55cbbc51d99eafce58bcd68809a20fd27267ef0fe62d3aa3a244e33e68992e
7
+ data.tar.gz: e39a90c28c7beab789885e53e35ae0ad43340ff4009f81ad1586ac16eb0073dbaf999b69a94f78e796f5bfe5e93a16e0a45481e76b706d0e496dfbe5435b4966
@@ -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
- d = []
72
- deps.each do |i|
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
- d << asset
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
- d = []
88
- deps.each do |i|
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
- d << asset
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 = [@source_file]
128
- all_dependenies(process_dependencies, Set.new, :process_dependencies) do |dep|
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 = [@source_file]
136
- all_dependenies(export_dependencies, Set.new, :export_dependencies) do |dep|
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
- @environment.base ? @source_file.delete_prefix(@environment.base) : @source_file,
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
174
  all_dependenies(process_dependencies, Set.new, :process_dependencies) do |dep|
157
175
  f << [
158
- @environment.base ? dep.source_file.delete_prefix(@environment.base) : dep.source_file,
176
+ normalize_filename_base(dep.source_file),
159
177
  Digest::SHA256.file(dep.source_file).hexdigest
160
178
  ]
161
179
  end
@@ -169,7 +187,7 @@ class Condenser
169
187
  f = []
170
188
  all_dependenies(export_dependencies, Set.new, :export_dependencies) do |dep|
171
189
  f << [
172
- @environment.base ? dep.source_file.delete_prefix(@environment.base) : dep.source_file,
190
+ normalize_filename_base(dep.source_file),
173
191
  Digest::SHA256.file(dep.source_file).hexdigest
174
192
  ]
175
193
  end
@@ -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
@@ -314,6 +334,17 @@ class Condenser
314
334
  @processed = true
315
335
  end
316
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
+
317
348
  def export
318
349
  return @export if @export
319
350
 
@@ -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
@@ -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
- assets.each do |asset|
127
- asset.all_process_dependencies.each do |pd|
128
- @process_dependencies[pd] ||= Set.new
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
- asset.all_export_dependencies.each do |pd|
133
- @export_dependencies[pd] ||= Set.new
134
- @export_dependencies[pd] << asset
143
+ @export_dependencies[pd] << asset
144
+ end
145
+ end
146
+ ensure
147
+ @fetching = nil
135
148
  end
136
149
  end
137
150
  end
@@ -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
@@ -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
- ExportDefaultDeclaration(path, state) {
98
+
99
+ ExportDeclaration(path, state) {
99
100
  hasExports = true;
100
- defaultExport = true;
101
+ if (path.node.source) {
102
+ imports.push(path.node.source.value);
103
+ }
101
104
  },
102
- ExportDefaultSpecifier(path, state) {
103
- hasExports = true;
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
  });
@@ -15,7 +15,7 @@ class Condenser::JSAnalyzer
15
15
  @source = input[:source]
16
16
  @stack = [:main]
17
17
 
18
- input[:export_dependencies] ||= []
18
+ input[:export_dependencies] ||= Set.new
19
19
 
20
20
  scan_until(/\A(\/\/[^\n]*(\n|\z))*/)
21
21
  if matched
@@ -28,6 +28,7 @@ class Condenser::JSAnalyzer
28
28
  end
29
29
 
30
30
  last_postion = nil
31
+ last_stack = nil
31
32
  while !eos?
32
33
  case @stack.last
33
34
 
@@ -53,6 +54,32 @@ class Condenser::JSAnalyzer
53
54
  scan_until(/(;|\n)/)
54
55
  @stack.pop
55
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
+
56
83
  else
57
84
  scan_until(/(\/\/|\/\*|\/|\(|\)|\{|\}|\"|\'|\`|export|import|\z)/)
58
85
 
@@ -85,15 +112,17 @@ class Condenser::JSAnalyzer
85
112
  @stack.push :brackets
86
113
  when '}'
87
114
  case @stack.last
88
- when :brackets, :tick_statment
115
+ when :tick_statment
116
+ @stack.pop
117
+ when :brackets
89
118
  @stack.pop
119
+ @stack << :export_from if @stack.last == :export
90
120
  else
91
121
  raise unexptected_token("}")
92
122
  end
93
123
  when 'export'
94
124
  if @stack.last == :main
95
- input[:exports] = true;
96
- input[:default_export] = true if next_word == 'default'
125
+ @stack << :export
97
126
  end
98
127
  when 'import'
99
128
  if @stack.last == :main
@@ -104,10 +133,13 @@ class Condenser::JSAnalyzer
104
133
  end
105
134
  end
106
135
 
107
- 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)
108
139
  raise Condenser::SyntaxError, "Error parsing JS file with JSAnalyzer"
109
140
  else
110
141
  last_postion = @index
142
+ last_stack = @stack.last
111
143
  end
112
144
  end
113
145
  end
@@ -121,7 +153,10 @@ class Condenser::JSAnalyzer
121
153
  message << "\n#{lineno.to_s.rjust(4)}: " << @source[start..uptop]
122
154
  message << "\n #{'-'* (@index-1-start)}#{'^'*(@matched.length)}"
123
155
  message << "\n"
124
- Condenser::SyntaxError.new(message)
156
+
157
+ syntax_error = Condenser::SyntaxError.new(message)
158
+ syntax_error.instance_variable_set(:@path, @sourcefile)
159
+ syntax_error
125
160
  end
126
161
 
127
162
  def double_quoted_value
@@ -60,6 +60,7 @@ class Condenser
60
60
  lineno = lines[0][/\((\d+):\d+\)$/, 1] if lines[0]
61
61
  lineno ||= 1
62
62
  error.set_backtrace(["#{source_file}:#{lineno}"] + caller)
63
+ error.instance_variable_set(:@path, source_file)
63
64
  error
64
65
  end
65
66
 
@@ -214,7 +214,7 @@ class Condenser::RollupProcessor < Condenser::NodeProcessor
214
214
  elsif importee.end_with?('*')
215
215
  File.join(File.dirname(importee), '*')
216
216
  else
217
- @environment.find(importee, importer ? File.dirname(@entry == importer ? @input[:source_file] : importer) : nil, accept: @input[:content_types].last)&.source_file
217
+ @environment.find(importee, importer ? (@entry == importer ? @input[:source_file] : importer) : nil, accept: @input[:content_types].last)&.source_file
218
218
  end
219
219
  when 'load'
220
220
  importee = message['args'].first
@@ -148,11 +148,34 @@ class Condenser
148
148
  end
149
149
  end
150
150
 
151
+ def has_dir?(path)
152
+ path.count('/') > (path.start_with?('/') ? 1 : 0)
153
+ end
154
+
155
+ def expand_path(path)
156
+ dir = if path.start_with?('/')
157
+ File.expand_path(path)
158
+ else
159
+ File.expand_path("/#{path}").delete_prefix('/')
160
+ end
161
+ dir.empty? ? nil : dir
162
+ end
163
+
151
164
  def decompose_path(path, base=nil)
152
- dirname = path.index('/') ? File.dirname(path) : nil
153
- if base && path&.start_with?('.')
154
- dirname = File.expand_path(dirname, base)
165
+ dirname = if base && path.start_with?('.')
166
+ if has_dir?(base)
167
+ if has_dir?(path)
168
+ expand_path(File.join(File.dirname(base), File.dirname(path)))
169
+ else
170
+ expand_path(File.dirname(base))
171
+ end
172
+ else
173
+ expand_path(File.dirname(path))
174
+ end
175
+ else
176
+ path.index('/') ? File.dirname(path) : nil
155
177
  end
178
+
156
179
 
157
180
  _, star, basename, extensions = path.match(/(([^\.\/]+)(\.[^\/]+)|\*|[^\/]+)$/).to_a
158
181
  if extensions == '.*'
@@ -1,3 +1,3 @@
1
1
  class Condenser
2
- VERSION = '1.3'
2
+ VERSION = '1.4'
3
3
  end
data/test/cache_test.rb CHANGED
@@ -60,6 +60,19 @@ class CacheTest < ActiveSupport::TestCase
60
60
  CSS
61
61
  end
62
62
 
63
+ test 'changing a source file on calls needs_reprocessing! and needs_reexporting! once' do
64
+ file 'test.txt.erb', "1<%= 1 + 1 %>3\n"
65
+ assert_file 'test.txt', 'text/plain', <<~CSS
66
+ 123
67
+ CSS
68
+
69
+ asset = @env.find('test.txt')
70
+
71
+ asset.expects(:needs_reprocessing!).once
72
+ asset.expects(:needs_reexporting!).once
73
+ file 'test.txt.erb', "1<%= 1 + 2 %>5\n"
74
+ end
75
+
63
76
  test 'changing a js dependency reflects in the next call' do
64
77
  file 'main.js', <<-JS
65
78
  import { cube } from 'math';
@@ -74,11 +87,11 @@ class CacheTest < ActiveSupport::TestCase
74
87
  JS
75
88
 
76
89
  assert_exported_file 'main.js', 'application/javascript', <<~CSS
77
- !function(){"use strict";var o;console.log((o=5)*o)}();
90
+ !function(){var o;console.log((o=5)*o)}();
78
91
  CSS
79
92
 
80
93
  assert_exported_file 'main.js', 'application/javascript', <<~CSS
81
- !function(){"use strict";var o;console.log((o=5)*o)}();
94
+ !function(){var o;console.log((o=5)*o)}();
82
95
  CSS
83
96
 
84
97
  file 'math.js', <<-JS
@@ -88,7 +101,7 @@ class CacheTest < ActiveSupport::TestCase
88
101
  JS
89
102
 
90
103
  assert_exported_file 'main.js', 'application/javascript', <<~CSS
91
- !function(){"use strict";var o;console.log((o=5)*o*o)}();
104
+ !function(){var o;console.log((o=5)*o*o)}();
92
105
  CSS
93
106
  end
94
107
 
@@ -123,10 +136,10 @@ class CacheTest < ActiveSupport::TestCase
123
136
  JS
124
137
 
125
138
  assert_exported_file 'a.js', 'application/javascript', <<~JS
126
- !function(){"use strict";console.log(5)}();
139
+ console.log(5);
127
140
  JS
128
141
  assert_exported_file 'b.js', 'application/javascript', <<~JS
129
- !function(){"use strict";console.log(5)}();
142
+ console.log(5);
130
143
  JS
131
144
 
132
145
  file 'dep.js', <<-JS
@@ -134,12 +147,11 @@ class CacheTest < ActiveSupport::TestCase
134
147
  JS
135
148
 
136
149
  assert_exported_file 'a.js', 'application/javascript', <<~JS
137
- !function(){"use strict";console.log(10)}();
150
+ console.log(10);
138
151
  JS
139
152
  assert_exported_file 'b.js', 'application/javascript', <<~JS
140
- !function(){"use strict";console.log(10)}();
153
+ console.log(10);
141
154
  JS
142
-
143
155
  end
144
156
 
145
157
  test 'a dependency is superceeded by a new file' do
@@ -158,7 +170,7 @@ class CacheTest < ActiveSupport::TestCase
158
170
  JS
159
171
 
160
172
  assert_exported_file 'a.js', 'application/javascript', <<~JS
161
- !function(){"use strict";console.log(5)}();
173
+ console.log(5);
162
174
  JS
163
175
 
164
176
  file 'a/dep.js', <<-JS
@@ -166,7 +178,7 @@ class CacheTest < ActiveSupport::TestCase
166
178
  JS
167
179
 
168
180
  assert_exported_file 'a.js', 'application/javascript', <<~JS
169
- !function(){"use strict";console.log(10)}();
181
+ console.log(10);
170
182
  JS
171
183
  end
172
184
 
@@ -186,7 +198,7 @@ class CacheTest < ActiveSupport::TestCase
186
198
  JS
187
199
 
188
200
  assert_exported_file 'a.js', 'application/javascript', <<~JS
189
- !function(){"use strict";console.log(5)}();
201
+ console.log(5);
190
202
  JS
191
203
 
192
204
  file 'a/deps/dep.js', <<-JS
@@ -194,7 +206,7 @@ class CacheTest < ActiveSupport::TestCase
194
206
  JS
195
207
 
196
208
  assert_exported_file 'a.js', 'application/javascript', <<~JS
197
- !function(){"use strict";console.log(10)}();
209
+ console.log(10);
198
210
  JS
199
211
 
200
212
  file 'a/deps/dep.js', <<-JS
@@ -202,7 +214,7 @@ class CacheTest < ActiveSupport::TestCase
202
214
  JS
203
215
 
204
216
  assert_exported_file 'a.js', 'application/javascript', <<~JS
205
- !function(){"use strict";console.log(20)}();
217
+ console.log(20);
206
218
  JS
207
219
  end
208
220
 
@@ -259,7 +271,7 @@ class CacheTest < ActiveSupport::TestCase
259
271
  JS
260
272
 
261
273
  assert_exported_file 'a.js', 'application/javascript', <<~JS
262
- !function(){"use strict";console.log("a")}();
274
+ console.log("a");
263
275
  JS
264
276
 
265
277
  file 'a.js', <<~JS
@@ -269,7 +281,7 @@ class CacheTest < ActiveSupport::TestCase
269
281
  JS
270
282
 
271
283
  assert_exported_file 'a.js', 'application/javascript', <<~JS
272
- !function(){"use strict";console.log("a"),console.log("b")}();
284
+ console.log("a"),console.log("b");
273
285
  JS
274
286
 
275
287
  file 'b.js', <<~JS
@@ -277,7 +289,7 @@ class CacheTest < ActiveSupport::TestCase
277
289
  JS
278
290
 
279
291
  assert_exported_file 'a.js', 'application/javascript', <<~JS
280
- !function(){"use strict";console.log("a"),console.log("c")}();
292
+ console.log("a"),console.log("c");
281
293
  JS
282
294
  end
283
295
 
@@ -308,5 +320,88 @@ class CacheTest < ActiveSupport::TestCase
308
320
  body{background:green}body{background:aqua}
309
321
  JS
310
322
  end
323
+
324
+ test 'ensure the build cache only walks the dependency tree once' do
325
+ # a
326
+ # | |
327
+ # b c
328
+ # |
329
+ # d
330
+
331
+ file 'd.js', "export default function d () { console.log('d'); }\n"
332
+ file 'b.js', "export default function b () { console.log('b'); }\n"
333
+ file 'c.js', <<~JS
334
+ import d from 'd';
335
+
336
+ export default function c () { console.log('c'); d(); }
337
+ JS
338
+ file 'a.js', <<~JS
339
+ import b from 'b';
340
+ import c from 'c';
341
+
342
+ console.log('a'); b(); c();
343
+ JS
344
+
345
+ assert_exported_file 'a.js', 'application/javascript', <<~JS
346
+ console.log("a"),console.log("b"),console.log("c"),console.log("d");
347
+ JS
348
+
349
+ file 'd.js', "export default function e () { console.log('e'); }\n"
350
+
351
+ pd = @env.build_cache.instance_variable_get(:@process_dependencies)
352
+ pd["#{@path}/a.js"] ||= Set.new
353
+ pd["#{@path}/b.js"] ||= Set.new
354
+ pd["#{@path}/c.js"] ||= Set.new
355
+ pd["#{@path}/d.js"] ||= Set.new
356
+ pd["#{@path}/a.js"].expects(:<<).with { |a| a.source_file == "#{@path}/a.js" }.once
357
+ pd["#{@path}/b.js"].expects(:<<).with { |a| a.source_file == "#{@path}/b.js" }.never
358
+ pd["#{@path}/c.js"].expects(:<<).with { |a| a.source_file == "#{@path}/c.js" }.never
359
+ pd["#{@path}/d.js"].expects(:<<).with { |a| a.source_file == "#{@path}/d.js" }.once
311
360
 
361
+ assert_exported_file 'a.js', 'application/javascript', <<~JS
362
+ console.log("a"),console.log("b"),console.log("c"),console.log("e");
363
+ JS
364
+ end
365
+
366
+ test 'same files in diffrent dirs sharing a cache doesnt poison the cache (ie capistrano deploys)' do
367
+ cachepath = Dir.mktmpdir
368
+
369
+ dir = File.realpath(Dir.mktmpdir)
370
+ base1 = File.join(dir, 'a')
371
+ base2 = File.join(dir, 'b')
372
+ base3 = File.join(dir, 'c')
373
+ Dir.mkdir(base1)
374
+ Dir.mkdir(base2)
375
+
376
+ [base1, base2, base3].each do |b|
377
+ file 'test.js', "export default function c () { console.log('t'); }\n", base: b
378
+ file 'test/b.js', <<~JS, base: b
379
+ import c from './c';
380
+ export default function b () { console.log('b'); c(); }
381
+ JS
382
+ file 'test/a.js', <<~JS, base: b
383
+ import t from 'test';
384
+ import b from './b';
385
+
386
+ console.log('a');
387
+ b();
388
+ JS
389
+ end
390
+
391
+ file 'test/c.js', "export default function c () { console.log('c'); }\n", base: base1
392
+ file 'test/c.js', "export default function c () { console.log('d'); }\n", base: base2
393
+ file 'test/c.js', "export default function c () { console.log('e'); }\n", base: base3
394
+
395
+ # Set the cache
396
+ env1 = Condenser.new(base1, logger: Logger.new(STDOUT, level: :debug), base: base1, npm_path: @npm_dir, cache: Condenser::Cache::FileStore.new(cachepath))
397
+ assert_equal 'console.log("a"),console.log("b"),console.log("c");', env1.find('test/a.js').export.source
398
+
399
+ # Poison the cache
400
+ env2 = Condenser.new(base2, logger: Logger.new(STDOUT, level: :debug), base: base2, npm_path: @npm_dir, cache: Condenser::Cache::FileStore.new(cachepath))
401
+ assert_equal 'console.log("a"),console.log("b"),console.log("d");', env2.find('test/a.js').export.source
402
+
403
+ # Fails to find dependency change because cache is missing the dependency
404
+ env3 = Condenser.new(base3, logger: Logger.new(STDOUT, level: :debug), base: base3, npm_path: @npm_dir, cache: Condenser::Cache::FileStore.new(cachepath))
405
+ assert_equal 'console.log("a"),console.log("b"),console.log("e");', env3.find('test/a.js').export.source
406
+ end
312
407
  end
@@ -17,7 +17,7 @@ class DependencyTest < ActiveSupport::TestCase
17
17
  JS
18
18
 
19
19
  asset = @env.find('name.js')
20
- assert_equal asset.instance_variable_get(:@process_dependencies).to_a, ["models/*.js","helpers/*.js"]
20
+ assert_equal asset.instance_variable_get(:@process_dependencies).to_a, [["models/*", ["application/javascript"]],["helpers/*", ["application/javascript"]]]
21
21
 
22
22
 
23
23
  assert_file 'name.js', 'application/javascript', <<~JS
@@ -54,7 +54,7 @@ class DependencyTest < ActiveSupport::TestCase
54
54
  JS
55
55
 
56
56
  asset = @env.find('name.js')
57
- assert_equal asset.instance_variable_get(:@process_dependencies).to_a, ["models/*.js","helpers/*.js"]
57
+ assert_equal asset.instance_variable_get(:@process_dependencies).to_a, [["models/*", ["application/javascript"]],["helpers/*", ["application/javascript"]]]
58
58
 
59
59
 
60
60
  assert_file 'name.js', 'application/javascript', <<~JS
@@ -74,4 +74,53 @@ class DependencyTest < ActiveSupport::TestCase
74
74
  JS
75
75
  end
76
76
 
77
+ test 'js depending on another file type with JSAnalzyer' do
78
+ @env.unregister_preprocessor 'application/javascript', Condenser::BabelProcessor
79
+ @env.register_preprocessor 'application/javascript', Condenser::JSAnalyzer
80
+ @env.unregister_minifier('application/javascript')
81
+
82
+ file 'a.js', ''
83
+ file 'b.rb', ''
84
+ file 'models/a.js', ''
85
+ file 'models/b.rb', ''
86
+
87
+ file 'name.js.erb', <<~JS
88
+ // depends_on **/*.rb
89
+
90
+ console.log([<%= Dir.children("#{@path}").sort.map(&:inspect).join(', ') %>]);
91
+ JS
92
+
93
+ asset = @env.find('name.js')
94
+ assert_equal asset.instance_variable_get(:@process_dependencies).to_a, [["**/*", ["application/ruby"]]]
95
+ assert_equal asset.process_dependencies.map(&:source_file), ["#{@path}/b.rb", "#{@path}/models/b.rb"]
96
+ end
97
+
98
+ test 'relative imports with JSAnalzyer' do
99
+ @env.unregister_preprocessor 'application/javascript', Condenser::BabelProcessor
100
+ @env.register_preprocessor 'application/javascript', Condenser::JSAnalyzer
101
+ @env.unregister_minifier('application/javascript')
102
+
103
+ file 'a/a.js', <<~JS
104
+ export decault function () { console.log("a/a"); }
105
+ JS
106
+ file 'b/a.js', <<~JS
107
+ export decault function () { console.log("a/a"); }
108
+ JS
109
+
110
+ file 'a/b.js', <<~JS
111
+ import fn from './a';
112
+ a();
113
+ console.log("a/b");
114
+ JS
115
+ file 'b/b.js', <<~JS
116
+ import fn from './a';
117
+ a();
118
+ console.log("b/b");
119
+ JS
120
+
121
+ asset = @env.find('a/b.js')
122
+ assert_equal asset.instance_variable_get(:@export_dependencies).to_a, [["#{@path}/a/a", ["application/javascript"]]]
123
+ assert_equal asset.export_dependencies.map(&:source_file), ["#{@path}/a/a.js"]
124
+ end
125
+
77
126
  end
@@ -28,6 +28,28 @@ class CondenserBabelTest < ActiveSupport::TestCase
28
28
  JS
29
29
  end
30
30
 
31
+ test "dependency tracking for a export from" do
32
+ file 'c.js', <<~JS
33
+ function c() { return 'ok'; }
34
+
35
+ export {c}
36
+ JS
37
+
38
+ file 'b.js', <<~JS
39
+ export {c} from 'c';
40
+
41
+ JS
42
+
43
+ file 'a.js', <<~JS
44
+ import {c} from 'b'
45
+
46
+ console.log(c());
47
+ JS
48
+
49
+ asset = assert_file 'a.js', 'application/javascript'
50
+ assert_equal ['/a.js', '/b.js', '/c.js'], asset.all_export_dependencies.map { |path| path.delete_prefix(@path) }
51
+ end
52
+
31
53
  test "error" do
32
54
  file 'error.js', <<~JS
33
55
  console.log('this file has an error');
@@ -47,6 +69,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
47
69
  | ^
48
70
  4 |
49
71
  ERROR
72
+ assert_equal '/assets/error.js', e.path
50
73
  end
51
74
 
52
75
  test 'not duplicating polyfills' do
@@ -83,7 +106,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
83
106
  };
84
107
 
85
108
  // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
86
- var global$9 =
109
+ var global$8 =
87
110
  // eslint-disable-next-line es/no-global-this -- safe
88
111
  check(typeof globalThis == 'object' && globalThis) ||
89
112
  check(typeof window == 'object' && window) ||
@@ -153,25 +176,14 @@ class CondenserBabelTest < ActiveSupport::TestCase
153
176
  if (classofRaw(fn) === 'Function') return uncurryThis$8(fn);
154
177
  };
155
178
 
156
- var documentAll$2 = typeof document == 'object' && document.all;
157
-
158
179
  // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
159
- // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing
160
- var IS_HTMLDDA = typeof documentAll$2 == 'undefined' && documentAll$2 !== undefined;
161
-
162
- var documentAll_1 = {
163
- all: documentAll$2,
164
- IS_HTMLDDA: IS_HTMLDDA
165
- };
166
-
167
- var $documentAll$1 = documentAll_1;
168
-
169
- var documentAll$1 = $documentAll$1.all;
180
+ var documentAll = typeof document == 'object' && document.all;
170
181
 
171
182
  // `IsCallable` abstract operation
172
183
  // https://tc39.es/ecma262/#sec-iscallable
173
- var isCallable$7 = $documentAll$1.IS_HTMLDDA ? function (argument) {
174
- return typeof argument == 'function' || argument === documentAll$1;
184
+ // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing
185
+ var isCallable$7 = typeof documentAll == 'undefined' && documentAll !== undefined ? function (argument) {
186
+ return typeof argument == 'function' || argument === documentAll;
175
187
  } : function (argument) {
176
188
  return typeof argument == 'function';
177
189
  };
@@ -261,20 +273,15 @@ class CondenserBabelTest < ActiveSupport::TestCase
261
273
  };
262
274
 
263
275
  var isCallable$6 = isCallable$7;
264
- var $documentAll = documentAll_1;
265
-
266
- var documentAll = $documentAll.all;
267
276
 
268
- var isObject$4 = $documentAll.IS_HTMLDDA ? function (it) {
269
- return typeof it == 'object' ? it !== null : isCallable$6(it) || it === documentAll;
270
- } : function (it) {
277
+ var isObject$4 = function (it) {
271
278
  return typeof it == 'object' ? it !== null : isCallable$6(it);
272
279
  };
273
280
 
274
281
  var path$3 = {};
275
282
 
276
283
  var path$2 = path$3;
277
- var global$8 = global$9;
284
+ var global$7 = global$8;
278
285
  var isCallable$5 = isCallable$7;
279
286
 
280
287
  var aFunction = function (variable) {
@@ -282,8 +289,8 @@ class CondenserBabelTest < ActiveSupport::TestCase
282
289
  };
283
290
 
284
291
  var getBuiltIn$1 = function (namespace, method) {
285
- return arguments.length < 2 ? aFunction(path$2[namespace]) || aFunction(global$8[namespace])
286
- : path$2[namespace] && path$2[namespace][method] || global$8[namespace] && global$8[namespace][method];
292
+ return arguments.length < 2 ? aFunction(path$2[namespace]) || aFunction(global$7[namespace])
293
+ : path$2[namespace] && path$2[namespace][method] || global$7[namespace] && global$7[namespace][method];
287
294
  };
288
295
 
289
296
  var uncurryThis$6 = functionUncurryThis;
@@ -292,11 +299,11 @@ class CondenserBabelTest < ActiveSupport::TestCase
292
299
 
293
300
  var engineUserAgent = typeof navigator != 'undefined' && String(navigator.userAgent) || '';
294
301
 
295
- var global$7 = global$9;
302
+ var global$6 = global$8;
296
303
  var userAgent = engineUserAgent;
297
304
 
298
- var process = global$7.process;
299
- var Deno = global$7.Deno;
305
+ var process = global$6.process;
306
+ var Deno = global$6.Deno;
300
307
  var versions = process && process.versions || Deno && Deno.version;
301
308
  var v8 = versions && versions.v8;
302
309
  var match, version;
@@ -323,9 +330,9 @@ class CondenserBabelTest < ActiveSupport::TestCase
323
330
  /* eslint-disable es/no-symbol -- required for testing */
324
331
  var V8_VERSION = engineV8Version;
325
332
  var fails$4 = fails$8;
326
- var global$6 = global$9;
333
+ var global$5 = global$8;
327
334
 
328
- var $String$2 = global$6.String;
335
+ var $String$2 = global$5.String;
329
336
 
330
337
  // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing
331
338
  var symbolConstructorDetection = !!Object.getOwnPropertySymbols && !fails$4(function () {
@@ -407,42 +414,42 @@ class CondenserBabelTest < ActiveSupport::TestCase
407
414
  throw new $TypeError$3("Can't convert object to primitive value");
408
415
  };
409
416
 
410
- var shared$1 = {exports: {}};
417
+ var sharedStore = {exports: {}};
411
418
 
412
- var global$5 = global$9;
419
+ var global$4 = global$8;
413
420
 
414
421
  // eslint-disable-next-line es/no-object-defineproperty -- safe
415
422
  var defineProperty$1 = Object.defineProperty;
416
423
 
417
424
  var defineGlobalProperty$1 = function (key, value) {
418
425
  try {
419
- defineProperty$1(global$5, key, { value: value, configurable: true, writable: true });
426
+ defineProperty$1(global$4, key, { value: value, configurable: true, writable: true });
420
427
  } catch (error) {
421
- global$5[key] = value;
428
+ global$4[key] = value;
422
429
  } return value;
423
430
  };
424
431
 
425
- var global$4 = global$9;
432
+ var globalThis$1 = global$8;
426
433
  var defineGlobalProperty = defineGlobalProperty$1;
427
434
 
428
435
  var SHARED = '__core-js_shared__';
429
- var store$1 = global$4[SHARED] || defineGlobalProperty(SHARED, {});
436
+ var store$1 = sharedStore.exports = globalThis$1[SHARED] || defineGlobalProperty(SHARED, {});
430
437
 
431
- var sharedStore = store$1;
432
-
433
- var store = sharedStore;
434
-
435
- (shared$1.exports = function (key, value) {
436
- return store[key] || (store[key] = value !== undefined ? value : {});
437
- })('versions', []).push({
438
- version: '3.34.0',
438
+ (store$1.versions || (store$1.versions = [])).push({
439
+ version: '3.37.1',
439
440
  mode: 'pure' ,
440
- copyright: '© 2014-2023 Denis Pushkarev (zloirock.ru)',
441
- license: 'https://github.com/zloirock/core-js/blob/v3.34.0/LICENSE',
441
+ copyright: '© 2014-2024 Denis Pushkarev (zloirock.ru)',
442
+ license: 'https://github.com/zloirock/core-js/blob/v3.37.1/LICENSE',
442
443
  source: 'https://github.com/zloirock/core-js'
443
444
  });
444
445
 
445
- var sharedExports = shared$1.exports;
446
+ var sharedStoreExports = sharedStore.exports;
447
+
448
+ var store = sharedStoreExports;
449
+
450
+ var shared$1 = function (key, value) {
451
+ return store[key] || (store[key] = value || {});
452
+ };
446
453
 
447
454
  var requireObjectCoercible = requireObjectCoercible$2;
448
455
 
@@ -476,8 +483,8 @@ class CondenserBabelTest < ActiveSupport::TestCase
476
483
  return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36);
477
484
  };
478
485
 
479
- var global$3 = global$9;
480
- var shared = sharedExports;
486
+ var global$3 = global$8;
487
+ var shared = shared$1;
481
488
  var hasOwn$3 = hasOwnProperty_1;
482
489
  var uid = uid$1;
483
490
  var NATIVE_SYMBOL = symbolConstructorDetection;
@@ -531,7 +538,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
531
538
  return isSymbol(key) ? key : key + '';
532
539
  };
533
540
 
534
- var global$2 = global$9;
541
+ var global$2 = global$8;
535
542
  var isObject$1 = isObject$4;
536
543
 
537
544
  var document$1 = global$2.document;
@@ -695,7 +702,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
695
702
  return object;
696
703
  };
697
704
 
698
- var global$1 = global$9;
705
+ var global$1 = global$8;
699
706
  var apply = functionApply;
700
707
  var uncurryThis$2 = functionUncurryThisClause;
701
708
  var isCallable = isCallable$7;
@@ -705,6 +712,8 @@ class CondenserBabelTest < ActiveSupport::TestCase
705
712
  var bind = functionBindContext;
706
713
  var createNonEnumerableProperty = createNonEnumerableProperty$1;
707
714
  var hasOwn$1 = hasOwnProperty_1;
715
+ // add debugging info
716
+
708
717
 
709
718
  var wrapConstructor = function (NativeConstructor) {
710
719
  var Wrapper = function (a, b, c) {
@@ -741,7 +750,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
741
750
  var STATIC = options.stat;
742
751
  var PROTO = options.proto;
743
752
 
744
- var nativeSource = GLOBAL ? global$1 : STATIC ? global$1[TARGET] : (global$1[TARGET] || {}).prototype;
753
+ var nativeSource = GLOBAL ? global$1 : STATIC ? global$1[TARGET] : global$1[TARGET] && global$1[TARGET].prototype;
745
754
 
746
755
  var target = GLOBAL ? path$1 : path$1[TARGET] || createNonEnumerableProperty(path$1, TARGET, {})[TARGET];
747
756
  var targetPrototype = target.prototype;
@@ -764,7 +773,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
764
773
  // export native or implementation
765
774
  sourceProperty = (USE_NATIVE && nativeProperty) ? nativeProperty : source[key];
766
775
 
767
- if (USE_NATIVE && typeof targetProperty == typeof sourceProperty) continue;
776
+ if (!FORCED && !PROTO && typeof targetProperty == typeof sourceProperty) continue;
768
777
 
769
778
  // bind methods to global for calling from export context
770
779
  if (options.bind && USE_NATIVE) resultProperty = bind(sourceProperty, global$1);
@@ -838,7 +847,8 @@ class CondenserBabelTest < ActiveSupport::TestCase
838
847
  // `ToLength` abstract operation
839
848
  // https://tc39.es/ecma262/#sec-tolength
840
849
  var toLength$1 = function (argument) {
841
- return argument > 0 ? min(toIntegerOrInfinity(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
850
+ var len = toIntegerOrInfinity(argument);
851
+ return len > 0 ? min(len, 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
842
852
  };
843
853
 
844
854
  var toLength = toLength$1;
@@ -858,6 +868,7 @@ class CondenserBabelTest < ActiveSupport::TestCase
858
868
  return function ($this, el, fromIndex) {
859
869
  var O = toIndexedObject$1($this);
860
870
  var length = lengthOfArrayLike(O);
871
+ if (length === 0) return !IS_INCLUDES && -1;
861
872
  var index = toAbsoluteIndex(fromIndex, length);
862
873
  var value;
863
874
  // Array#includes uses SameValueZero equality algorithm
@@ -248,4 +248,39 @@ class JSAnalyzerTest < ActiveSupport::TestCase
248
248
  assert_equal ['a.js', 'b.js'], asset.export_dependencies.map(&:filename)
249
249
  end
250
250
 
251
+ test "dependency tracking for a export from" do
252
+ file 'c.js', <<~JS
253
+ function c() { return 'ok'; }
254
+
255
+ export {c}
256
+ JS
257
+
258
+ file 'b.js', <<~JS
259
+ export {c} from 'c';
260
+
261
+ JS
262
+
263
+ file 'a.js', <<~JS
264
+ import {c} from 'b'
265
+
266
+ console.log(c());
267
+ JS
268
+
269
+ asset = assert_file 'a.js', 'application/javascript'
270
+ assert_equal ['/a.js', '/b.js', '/c.js'], asset.all_export_dependencies.map { |path| path.delete_prefix(@path) }
271
+ end
272
+
273
+ test 'exporting a nested object' do
274
+ file 't.js', <<~JS
275
+ export default {
276
+ registry: {
277
+ boolean: true,
278
+ integer: 1
279
+ }
280
+ };
281
+ JS
282
+
283
+ asset = assert_file 't.js', 'application/javascript'
284
+ end
285
+
251
286
  end
data/test/resolve_test.rb CHANGED
@@ -35,6 +35,16 @@ class ResolveTest < ActiveSupport::TestCase
35
35
  assert_equal ['test', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/*.js')
36
36
  assert_equal ['test/*', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/*/*.js')
37
37
  assert_equal ['test/**', '*', ['.js'], ['application/javascript']], @env.decompose_path('test/**/*.js')
38
+
39
+ assert_equal ['a', 'test', ['.js'], ['application/javascript']], @env.decompose_path('./test.js', 'a/b.js')
40
+ assert_equal [nil, 'test', ['.js'], ['application/javascript']], @env.decompose_path('./test.js', 'b.js')
41
+ assert_equal ['a/folder', 'test', ['.js'], ['application/javascript']], @env.decompose_path('./folder/test.js', 'a/b.js')
42
+ assert_equal ['folder', 'test', ['.js'], ['application/javascript']], @env.decompose_path('./folder/test.js', 'b.js')
43
+
44
+ assert_equal [nil, 'test', ['.js'], ['application/javascript']], @env.decompose_path('../test.js', 'a/b.js')
45
+ assert_equal ["/", 'test', ['.js'], ['application/javascript']], @env.decompose_path('../test.js', '/a/b.js')
46
+ assert_equal ["folder", 'test', ['.js'], ['application/javascript']], @env.decompose_path('../folder/test.js', 'a/b.js')
47
+ assert_equal ["/folder", 'test', ['.js'], ['application/javascript']], @env.decompose_path('../folder/test.js', '/a/b.js')
38
48
  end
39
49
 
40
50
  test 'resolve' do
data/test/server_test.rb CHANGED
@@ -27,10 +27,10 @@ class ServerTest < ActiveSupport::TestCase
27
27
  test "serve single source file" do
28
28
  get "/assets/foo.js"
29
29
  assert_equal 200, last_response.status
30
- assert_equal "43", last_response.headers['Content-Length']
30
+ assert_equal "15", last_response.headers['Content-Length']
31
31
  assert_equal "Accept-Encoding", last_response.headers['Vary']
32
32
  assert_equal <<~JS.strip, last_response.body
33
- !function(){"use strict";console.log(1)}();
33
+ console.log(1);
34
34
  JS
35
35
  end
36
36
 
@@ -54,7 +54,7 @@ class ServerTest < ActiveSupport::TestCase
54
54
 
55
55
  get "/assets/main.js"
56
56
  assert_equal <<~JS.strip, last_response.body
57
- !function(){"use strict";var o;console.log((o=5)*o*o)}();
57
+ !function(){var o;console.log((o=5)*o*o)}();
58
58
  JS
59
59
  end
60
60
 
@@ -73,7 +73,7 @@ class ServerTest < ActiveSupport::TestCase
73
73
  test "serve source with etag headers" do
74
74
  get "/assets/foo.js"
75
75
 
76
- digest = '9001f422e9c91516f6e130be56dd77aa313c52bb7dd18e4bb085067162c9fa70'
76
+ digest = '35c146f76e129477c64061bc84511e1090f3d4d8059713e6663dd4b35b1f7642'
77
77
  assert_equal "\"#{digest}\"", last_response.headers['ETag']
78
78
  end
79
79
 
@@ -107,8 +107,8 @@ class ServerTest < ActiveSupport::TestCase
107
107
  'HTTP_IF_NONE_MATCH' => "nope"
108
108
 
109
109
  assert_equal 200, last_response.status
110
- assert_equal '"9001f422e9c91516f6e130be56dd77aa313c52bb7dd18e4bb085067162c9fa70"', last_response.headers['ETag']
111
- assert_equal '43', last_response.headers['Content-Length']
110
+ assert_equal '"35c146f76e129477c64061bc84511e1090f3d4d8059713e6663dd4b35b1f7642"', last_response.headers['ETag']
111
+ assert_equal '15', last_response.headers['Content-Length']
112
112
  end
113
113
 
114
114
  test "not modified partial response with fingerprint and if-none-match etags match" do
@@ -167,8 +167,8 @@ class ServerTest < ActiveSupport::TestCase
167
167
  'HTTP_IF_MATCH' => etag
168
168
 
169
169
  assert_equal 200, last_response.status
170
- assert_equal '"9001f422e9c91516f6e130be56dd77aa313c52bb7dd18e4bb085067162c9fa70"', last_response.headers['ETag']
171
- assert_equal '43', last_response.headers['Content-Length']
170
+ assert_equal '"35c146f76e129477c64061bc84511e1090f3d4d8059713e6663dd4b35b1f7642"', last_response.headers['ETag']
171
+ assert_equal '15', last_response.headers['Content-Length']
172
172
  end
173
173
 
174
174
  test "precondition failed with if-match is a mismatch" do
@@ -253,7 +253,7 @@ class ServerTest < ActiveSupport::TestCase
253
253
  JS
254
254
  get "/assets/%E6%97%A5%E6%9C%AC%E8%AA%9E.js"
255
255
  assert_equal <<~JS.strip, last_response.body
256
- !function(){"use strict";console.log("日本語")}();
256
+ console.log("日本語");
257
257
  JS
258
258
  end
259
259
 
data/test/test_helper.rb CHANGED
@@ -53,9 +53,10 @@ class ActiveSupport::TestCase
53
53
  assert_equal json, jbuild(&block)
54
54
  end
55
55
 
56
- def file(name, source)
57
- dir = name.include?('/') ? File.join(@path, File.dirname(name)) : @path
58
- path = File.join(@path, name)
56
+ def file(name, source, base: nil)
57
+ base ||= @path
58
+ dir = name.include?('/') ? File.join(base, File.dirname(name)) : base
59
+ path = File.join(base, name)
59
60
 
60
61
  FileUtils.mkdir_p(dir)
61
62
  if File.exist?(path)
@@ -78,6 +79,7 @@ class ActiveSupport::TestCase
78
79
  assert_equal path.delete_prefix('/'), asset.filename
79
80
  assert_equal Array(mime_types), asset.content_types
80
81
  assert_equal(source.rstrip, asset.source.rstrip) if !source.nil?
82
+ asset
81
83
  end
82
84
 
83
85
  def assert_exported_file(path, mime_types, source=nil)
@@ -88,6 +90,7 @@ class ActiveSupport::TestCase
88
90
  assert_equal path, asset.filename
89
91
  assert_equal Array(mime_types), asset.content_types
90
92
  assert_equal(source.rstrip, asset.source.rstrip) if !source.nil?
93
+ asset
91
94
  end
92
95
 
93
96
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: condenser
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.3'
4
+ version: '1.4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-06 00:00:00.000000000 Z
11
+ date: 2024-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubi
@@ -398,7 +398,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
398
398
  - !ruby/object:Gem::Version
399
399
  version: '0'
400
400
  requirements: []
401
- rubygems_version: 3.4.13
401
+ rubygems_version: 3.5.4
402
402
  signing_key:
403
403
  specification_version: 4
404
404
  summary: A Rack-based asset packaging system