spoom 1.8.10 → 1.8.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 616b67329b164de71785d14b0354c5b4567aa3d062c522f4bf1f8348b33eb5de
4
- data.tar.gz: f89cde916059f3d5c21fb86a702bccc2817adbbf95da25a0201ea19c420e8c93
3
+ metadata.gz: 284bf38248b3fb6c057966d56d0bbcd01e3fa4142e6a619ffacb98eb159a6d7d
4
+ data.tar.gz: 44551f71e890cde0d0a71e77b8a4d627c7aafbddaa8e0e3a7106135c6962398a
5
5
  SHA512:
6
- metadata.gz: d08700dba2f90d25267122d4fbfc5949cc8fd2e3a1480282e42784d95f6acdb99e731062e83ea55fde23a60a1db87e191fc6cfe37cc9a3300b8539a125fd5a9f
7
- data.tar.gz: df7367580d6969dbb3a1bc609f5a4a3564ddf3a054d49cde6ef7d7cf68dac51c21d3dabd2c35dae1e82573ead6f7e4971d0098f2b90fb378d3b3dd6674f66582
6
+ metadata.gz: 60c251eaa56b3ad7ffac08afe381203869146040b144c7893013dcb098fc9150bad9a05b49bc303919946904b0757a6ddd285282a80bf64f607630d942b57d19
7
+ data.tar.gz: 462f445ffb6b740ef17486e52d3e3c65d000f482199206ea3ef6e39c7a9aa7183c9df8097b73967f89f3db5c88d3e751edba61c3fb691f2909a48930a0d78b5e
data/README.md CHANGED
@@ -387,6 +387,20 @@ Remove a reported dead code candidate:
387
387
  spoom deadcode remove path/to/file.rb:42:18-47:23
388
388
  ```
389
389
 
390
+ #### Custom plugins
391
+
392
+ Teach `spoom deadcode` about your project's own DSLs by adding plugins to `.spoom/deadcode/plugins/*.rb`. Each file should define a subclass of `Spoom::Deadcode::Plugins::Base`:
393
+
394
+ ```ruby
395
+ # .spoom/deadcode/plugins/my_plugin.rb
396
+ class MyPlugin < Spoom::Deadcode::Plugins::Base
397
+ ignore_methods_named("my_dsl_method")
398
+ end
399
+ ```
400
+
401
+ > [!WARNING]
402
+ > Custom plugins are loaded with `require`, so `spoom deadcode` executes any Ruby code in `.spoom/deadcode/plugins/` of the project being analyzed. Only run `spoom deadcode` on projects you trust, and inspect or remove `.spoom/deadcode/plugins/` before analyzing untrusted code (such as a pull request from an outside contributor).
403
+
390
404
  ## Ruby API
391
405
 
392
406
  ### Parsing Sorbet config
@@ -73,7 +73,18 @@ module Spoom
73
73
  end
74
74
 
75
75
  plugin_classes = Spoom::Deadcode.plugins_from_gemfile_lock(context)
76
- plugin_classes.merge(Spoom::Deadcode.load_custom_plugins(context))
76
+
77
+ custom_plugin_files = context.glob("#{Spoom::Deadcode::DEFAULT_CUSTOM_PLUGINS_PATH}/*.rb")
78
+ unless custom_plugin_files.empty?
79
+ say_warning(
80
+ "Loading custom plugins from `#{Spoom::Deadcode::DEFAULT_CUSTOM_PLUGINS_PATH}`. " \
81
+ "This executes Ruby code from the analyzed project:",
82
+ )
83
+ custom_plugin_files.each do |file|
84
+ $stderr.puts " #{gray(file)}"
85
+ end
86
+ plugin_classes.merge(Spoom::Deadcode.load_custom_plugins(context))
87
+ end
77
88
  if options[:show_plugins]
78
89
  $stderr.puts "\nLoaded #{blue(plugin_classes.size.to_s)} plugins\n"
79
90
  plugin_classes.each do |plugin|
@@ -153,6 +153,9 @@ module Spoom
153
153
  snapshots = files.sort.map do |file|
154
154
  json = File.read(file)
155
155
  Spoom::Coverage::Snapshot.from_json(json)
156
+ rescue Spoom::Coverage::Snapshot::Error => e
157
+ say_error("Invalid snapshot `#{file}`: #{e.message}")
158
+ exit(1)
156
159
  end.filter(&:commit_timestamp).sort_by!(&:commit_timestamp)
157
160
 
158
161
  palette = Spoom::Coverage::D3::ColorPalette.new(
@@ -6,6 +6,9 @@ module Spoom
6
6
  module D3
7
7
  # @abstract
8
8
  class Base
9
+ # Characters that must not appear raw inside a `<script>` element, and their JSON string escapes
10
+ JS_LITERAL_ESCAPES = { "<" => '\u003c', ">" => '\u003e', "&" => '\u0026' }.freeze #: Hash[String, String]
11
+
9
12
  #: String
10
13
  attr_reader :id
11
14
 
@@ -43,6 +46,18 @@ module Spoom
43
46
  # @abstract
44
47
  #: -> String
45
48
  def script = raise NotImplementedError, "Abstract method called"
49
+
50
+ private
51
+
52
+ # Serialize `value` as a JavaScript literal that is safe to embed in a `<script>` tag
53
+ #
54
+ # `script_safe` escapes `/` so the data can't contain `</script>`. We also escape `<`, `>` and `&`
55
+ # so the data can't contain `<!--` or `<script`, which change how the HTML parser reads the rest
56
+ # of the `<script>` element.
57
+ #: (untyped value) -> String
58
+ def js_literal(value)
59
+ JSON.generate(value, script_safe: true).gsub(/[<>&]/, JS_LITERAL_ESCAPES)
60
+ end
46
61
  end
47
62
  end
48
63
  end
@@ -47,7 +47,7 @@ module Spoom
47
47
 
48
48
  function tooltipMap(d) {
49
49
  moveTooltip(d)
50
- .html("<b>" + d.data.name + "</b>")
50
+ .html("<b>" + escapeHtml(d.data.name) + "</b>")
51
51
  }
52
52
  JS
53
53
  end
@@ -57,7 +57,7 @@ module Spoom
57
57
  #: -> String
58
58
  def script
59
59
  <<~JS
60
- var root = {children: #{@data.to_json}}
60
+ var root = {children: #{js_literal(@data)}}
61
61
  var dataHeight = treeHeight(root)
62
62
 
63
63
  var opacity = d3.scaleLinear()
@@ -38,8 +38,8 @@ module Spoom
38
38
  <<~JS
39
39
  function tooltipPie(d, title, kind, sum) {
40
40
  moveTooltip(d)
41
- .html("<b>" + title + "</b><br><br>"
42
- + "<b>" + d.data.value + "</b> " + kind + "<br>"
41
+ .html("<b>" + escapeHtml(title) + "</b><br><br>"
42
+ + "<b>" + escapeHtml(d.data.value) + "</b> " + kind + "<br>"
43
43
  + "<b>" + toPercent(d.data.value, sum) + "</b>%")
44
44
  }
45
45
  JS
@@ -52,11 +52,11 @@ module Spoom
52
52
  <<~JS
53
53
  #{tooltip}
54
54
 
55
- var json_#{id} = #{@data.to_json};
55
+ var json_#{id} = #{js_literal(@data)};
56
56
  var pie_#{id} = d3.pie().value((d) => d.value);
57
57
  var data_#{id} = pie_#{id}(d3.entries(json_#{id}));
58
58
  var sum_#{id} = d3.sum(data_#{id}, (d) => d.data.value);
59
- var title_#{id} = #{@title.to_json};
59
+ var title_#{id} = #{js_literal(@title)};
60
60
 
61
61
  function draw_#{id}() {
62
62
  var pieSize_#{id} = document.getElementById("#{id}").clientWidth - 10;
@@ -81,10 +81,10 @@ module Spoom
81
81
 
82
82
  function tooltipTimeline(d, kind) {
83
83
  moveTooltip(d)
84
- .html("commit <b>" + d.data.commit + "</b><br>"
84
+ .html("commit <b>" + escapeHtml(d.data.commit) + "</b><br>"
85
85
  + d3.timeFormat("%y/%m/%d")(parseDate(d.data.timestamp)) + "<br><br>"
86
- + "<b>typed: " + d.key + "</b><br><br>"
87
- + "<b>" + (d.data.values[d.key] ? d.data.values[d.key] : 0) + "</b> " + kind +"<br>"
86
+ + "<b>typed: " + escapeHtml(d.key) + "</b><br><br>"
87
+ + "<b>" + escapeHtml(d.data.values[d.key] ? d.data.values[d.key] : 0) + "</b> " + kind +"<br>"
88
88
  + "<b>" + toPercent(d.data.values[d.key] ? d.data.values[d.key] : 0, d.data.total) + "%")
89
89
  }
90
90
  JS
@@ -97,7 +97,7 @@ module Spoom
97
97
  <<~HTML
98
98
  #{tooltip}
99
99
 
100
- var data_#{id} = #{@data.to_json};
100
+ var data_#{id} = #{js_literal(@data)};
101
101
 
102
102
  function draw_#{id}() {
103
103
  var width_#{id} = document.getElementById("#{id}").clientWidth;
@@ -245,10 +245,10 @@ module Spoom
245
245
  <<~JS
246
246
  function tooltip_#{id}(d) {
247
247
  moveTooltip(d)
248
- .html("commit <b>" + d.commit + "</b><br>"
248
+ .html("commit <b>" + escapeHtml(d.commit) + "</b><br>"
249
249
  + d3.timeFormat("%y/%m/%d")(parseDate(d.timestamp)) + "<br><br>"
250
- + "static: v<b>" + d.static + "</b><br>"
251
- + "runtime: v<b>" + d.runtime + "</b><br><br>"
250
+ + "static: v<b>" + escapeHtml(d.static) + "</b><br>"
251
+ + "runtime: v<b>" + escapeHtml(d.runtime) + "</b><br><br>"
252
252
  + "versions from<br>Gemfile.lock")
253
253
  }
254
254
  JS
@@ -294,9 +294,9 @@ module Spoom
294
294
  <<~JS
295
295
  function tooltip_#{id}(d) {
296
296
  moveTooltip(d)
297
- .html("commit <b>" + d.commit + "</b><br>"
297
+ .html("commit <b>" + escapeHtml(d.commit) + "</b><br>"
298
298
  + d3.timeFormat("%y/%m/%d")(parseDate(d.timestamp)) + "<br><br>"
299
- + "<b>" + d.runtime + "</b>s<br><br>"
299
+ + "<b>" + escapeHtml(d.runtime) + "</b>s<br><br>"
300
300
  + "(sorbet user + system time)")
301
301
  }
302
302
  JS
@@ -330,8 +330,8 @@ module Spoom
330
330
  <<~JS
331
331
  #{tooltip}
332
332
 
333
- var data_#{id} = #{@data.to_json};
334
- var keys_#{id} = #{T.unsafe(@keys).to_json};
333
+ var data_#{id} = #{js_literal(@data)};
334
+ var keys_#{id} = #{js_literal(@keys)};
335
335
 
336
336
  var stack_#{id} = d3.stack()
337
337
  .keys(keys_#{id})
@@ -515,11 +515,11 @@ module Spoom
515
515
  <<~JS
516
516
  function tooltip_#{id}(d) {
517
517
  moveTooltip(d)
518
- .html("commit <b>" + d.data.commit + "</b><br>"
518
+ .html("commit <b>" + escapeHtml(d.data.commit) + "</b><br>"
519
519
  + d3.timeFormat("%y/%m/%d")(parseDate(d.data.timestamp)) + "<br><br>"
520
- + "Files: <b>" + d.data.values.files + "</b><br>"
521
- + "RBIs: <b>" + d.data.values.rbis + "</b><br><br>"
522
- + "Total: <b>" + d.data.total + "</b>")
520
+ + "Files: <b>" + escapeHtml(d.data.values.files) + "</b><br>"
521
+ + "RBIs: <b>" + escapeHtml(d.data.values.rbis) + "</b><br><br>"
522
+ + "Total: <b>" + escapeHtml(d.data.total) + "</b>")
523
523
  }
524
524
  JS
525
525
  end
@@ -530,8 +530,8 @@ module Spoom
530
530
  <<~JS
531
531
  #{tooltip}
532
532
 
533
- var data_#{id} = #{@data.to_json};
534
- var keys_#{id} = #{T.unsafe(@keys).to_json};
533
+ var data_#{id} = #{js_literal(@data)};
534
+ var keys_#{id} = #{js_literal(@keys)};
535
535
 
536
536
  var stack_#{id} = d3.stack()
537
537
  .keys(keys_#{id})
@@ -76,6 +76,15 @@ module Spoom
76
76
  return "#{palette.false_color}";
77
77
  }
78
78
 
79
+ function escapeHtml(value) {
80
+ return String(value)
81
+ .replace(/&/g, "&amp;")
82
+ .replace(/</g, "&lt;")
83
+ .replace(/>/g, "&gt;")
84
+ .replace(/"/g, "&quot;")
85
+ .replace(/'/g, "&#39;");
86
+ }
87
+
79
88
  function toPercent(value, sum) {
80
89
  return value ? Math.round(value * 100 / sum) : 0;
81
90
  }
@@ -60,7 +60,7 @@ module Spoom
60
60
 
61
61
  #: -> String
62
62
  def header_html
63
- "<h1 class='display-3'>#{title}</h1>"
63
+ "<h1 class='display-3'>#{ERB::Util.html_escape(title)}</h1>"
64
64
  end
65
65
 
66
66
  #: -> String
@@ -219,7 +219,7 @@ module Spoom
219
219
  <<~ERB
220
220
  <div class="text-center" style="margin-top: 30px">
221
221
  Typchecked by Sorbet since <b>#{@sorbet_intro_date&.strftime("%F")}</b>
222
- (commit <b>#{@sorbet_intro_commit}</b>).
222
+ (commit <b>#{ERB::Util.html_escape(@sorbet_intro_commit)}</b>).
223
223
  </div>
224
224
  ERB
225
225
  end
@@ -263,8 +263,8 @@ module Spoom
263
263
  last = T.must(@snapshots.last)
264
264
  <<~ERB
265
265
  <h1 class="display-3">
266
- #{@project_name}
267
- <span class="badge badge-pill badge-dark" style="font-size: 20%;">#{last.commit_sha}</span>
266
+ #{ERB::Util.html_escape(@project_name)}
267
+ <span class="badge badge-pill badge-dark" style="font-size: 20%;">#{ERB::Util.html_escape(last.commit_sha)}</span>
268
268
  </h1>
269
269
  ERB
270
270
  end
@@ -4,6 +4,8 @@
4
4
  module Spoom
5
5
  module Coverage
6
6
  class Snapshot
7
+ class Error < Spoom::Error; end
8
+
7
9
  #: Integer
8
10
  attr_accessor :timestamp
9
11
 
@@ -173,30 +175,32 @@ module Spoom
173
175
  #: (Hash[String, untyped] obj) -> Snapshot
174
176
  def from_obj(obj)
175
177
  snapshot = Snapshot.new
176
- snapshot.timestamp = obj.fetch("timestamp", 0)
177
- snapshot.version_static = obj.fetch("version_static", nil)
178
- snapshot.version_runtime = obj.fetch("version_runtime", nil)
179
- snapshot.duration = obj.fetch("duration", 0)
180
- snapshot.commit_sha = obj.fetch("commit_sha", nil)
181
- snapshot.commit_timestamp = obj.fetch("commit_timestamp", nil)
182
- snapshot.files = obj.fetch("files", 0)
183
- snapshot.rbi_files = obj.fetch("rbi_files", 0)
184
- snapshot.modules = obj.fetch("modules", 0)
185
- snapshot.classes = obj.fetch("classes", 0)
186
- snapshot.singleton_classes = obj.fetch("singleton_classes", 0)
187
- snapshot.methods_with_sig = obj.fetch("methods_with_sig", 0)
188
- snapshot.methods_without_sig = obj.fetch("methods_without_sig", 0)
189
- snapshot.calls_typed = obj.fetch("calls_typed", 0)
190
- snapshot.calls_untyped = obj.fetch("calls_untyped", 0)
191
- snapshot.methods_with_sig_excluding_rbis = obj.fetch("methods_with_sig_excluding_rbis", 0)
192
- snapshot.methods_without_sig_excluding_rbis = obj.fetch("methods_without_sig_excluding_rbis", 0)
178
+ snapshot.timestamp = integer_field(obj, "timestamp")
179
+ snapshot.version_static = obj.fetch("version_static", nil)&.to_s
180
+ snapshot.version_runtime = obj.fetch("version_runtime", nil)&.to_s
181
+ snapshot.duration = integer_field(obj, "duration")
182
+ snapshot.commit_sha = obj.fetch("commit_sha", nil)&.to_s
183
+ unless obj.fetch("commit_timestamp", nil).nil?
184
+ snapshot.commit_timestamp = integer_field(obj, "commit_timestamp")
185
+ end
186
+ snapshot.files = integer_field(obj, "files")
187
+ snapshot.rbi_files = integer_field(obj, "rbi_files")
188
+ snapshot.modules = integer_field(obj, "modules")
189
+ snapshot.classes = integer_field(obj, "classes")
190
+ snapshot.singleton_classes = integer_field(obj, "singleton_classes")
191
+ snapshot.methods_with_sig = integer_field(obj, "methods_with_sig")
192
+ snapshot.methods_without_sig = integer_field(obj, "methods_without_sig")
193
+ snapshot.calls_typed = integer_field(obj, "calls_typed")
194
+ snapshot.calls_untyped = integer_field(obj, "calls_untyped")
195
+ snapshot.methods_with_sig_excluding_rbis = integer_field(obj, "methods_with_sig_excluding_rbis")
196
+ snapshot.methods_without_sig_excluding_rbis = integer_field(obj, "methods_without_sig_excluding_rbis")
193
197
 
194
198
  sigils = obj.fetch("sigils", {})
195
199
  if sigils
196
200
  Snapshot::STRICTNESSES.each do |strictness|
197
201
  next unless sigils.key?(strictness)
198
202
 
199
- snapshot.sigils[strictness] = sigils[strictness]
203
+ snapshot.sigils[strictness] = integer_field(sigils, strictness)
200
204
  end
201
205
  end
202
206
 
@@ -205,12 +209,29 @@ module Spoom
205
209
  Snapshot::STRICTNESSES.each do |strictness|
206
210
  next unless sigils_excluding_rbis.key?(strictness)
207
211
 
208
- snapshot.sigils_excluding_rbis[strictness] = sigils_excluding_rbis[strictness]
212
+ snapshot.sigils_excluding_rbis[strictness] = integer_field(sigils_excluding_rbis, strictness)
209
213
  end
210
214
  end
211
215
 
212
216
  snapshot
213
217
  end
218
+
219
+ private
220
+
221
+ # Read `key` from `obj` as an Integer
222
+ #
223
+ # Snapshot files live in the analyzed project and can't be trusted to hold the types spoom wrote.
224
+ # Raises `Snapshot::Error` if the value can't be interpreted as an Integer.
225
+ #: (Hash[String, untyped] obj, String key, ?default: Integer) -> Integer
226
+ def integer_field(obj, key, default: 0)
227
+ if (value = obj[key])
228
+ Integer(value)
229
+ else
230
+ default
231
+ end
232
+ rescue ArgumentError, TypeError
233
+ raise Error, "Expected an integer for `#{key}`, got #{value.inspect}"
234
+ end
214
235
  end
215
236
  end
216
237
 
@@ -51,6 +51,21 @@ module Spoom
51
51
  !!strictness && valid_strictness?(strictness)
52
52
  end
53
53
 
54
+ # returns true if the passed content contains a valid sigil at or above the passed strictness
55
+ # internal sigils do not satisfy a minimum strictness
56
+ #: (String content, String strictness) -> bool
57
+ def satisfies_minimum_strictness?(content, strictness)
58
+ return false unless contains_valid_sigil?(content)
59
+
60
+ current_strictness = strictness_in_content(content)
61
+ strictness = strictness.strip
62
+ return false if current_strictness == STRICTNESS_INTERNAL || strictness == STRICTNESS_INTERNAL
63
+
64
+ strictness_index = VALID_STRICTNESS.index(current_strictness)
65
+ minimum_index = VALID_STRICTNESS.index(strictness)
66
+ !!strictness_index && !!minimum_index && strictness_index >= minimum_index
67
+ end
68
+
54
69
  # returns a string which is the passed content but with the sigil updated to a new strictness
55
70
  #: (String content, String new_strictness) -> String
56
71
  def update_sigil(content, new_strictness)
@@ -6,7 +6,9 @@ module Spoom
6
6
  module Translate
7
7
  module RBSCommentsToSorbetSigs
8
8
  class << self
9
- RBS_ANNOTATION_MARKERS = [
9
+ RBS_REWRITE_PATTERN = Regexp.union([
10
+ "#:",
11
+ "#|",
10
12
  "# @abstract",
11
13
  "# @interface",
12
14
  "# @sealed",
@@ -15,9 +17,16 @@ module Spoom
15
17
  "# @override",
16
18
  "# @overridable",
17
19
  "# @without_runtime",
18
- ].freeze #: Array[String]
19
- RBS_REWRITE_PATTERN = Regexp.union(["#:", "#|", *RBS_ANNOTATION_MARKERS]).freeze #: Regexp
20
- private_constant :RBS_ANNOTATION_MARKERS, :RBS_REWRITE_PATTERN
20
+ ]).freeze #: Regexp
21
+ private_constant :RBS_REWRITE_PATTERN
22
+
23
+ # Detects RBS comment markers (`#:`, `#|`, and supported `# @...` annotations).
24
+ # Use this check to skip parsing files without those markers.
25
+ # It does not validate RBS syntax or check the `typed:` sigil.
26
+ #: (String ruby_contents) -> bool
27
+ def contains_rbs_syntax?(ruby_contents)
28
+ ruby_contents.match?(RBS_REWRITE_PATTERN)
29
+ end
21
30
 
22
31
  #: (
23
32
  #| String ruby_contents,
@@ -34,7 +43,7 @@ module Spoom
34
43
  erase_generic_types: false,
35
44
  force: false
36
45
  )
37
- return ruby_contents unless ruby_contents.match?(RBS_REWRITE_PATTERN) &&
46
+ return ruby_contents unless contains_rbs_syntax?(ruby_contents) &&
38
47
  (force || Sigils.contains_valid_sigil?(ruby_contents))
39
48
 
40
49
  options = Options.new(
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.8.10"
5
+ VERSION = "1.8.12"
6
6
  end
data/rbi/spoom.rbi CHANGED
@@ -614,6 +614,11 @@ class Spoom::Coverage::D3::Base
614
614
  sig { returns(::String) }
615
615
  def tooltip; end
616
616
 
617
+ private
618
+
619
+ sig { params(value: T.untyped).returns(::String) }
620
+ def js_literal(value); end
621
+
617
622
  class << self
618
623
  sig { returns(::String) }
619
624
  def header_script; end
@@ -623,6 +628,7 @@ class Spoom::Coverage::D3::Base
623
628
  end
624
629
  end
625
630
 
631
+ Spoom::Coverage::D3::Base::JS_LITERAL_ESCAPES = T.let(T.unsafe(nil), Hash)
626
632
  Spoom::Coverage::D3::COLOR_FALSE = T.let(T.unsafe(nil), String)
627
633
  Spoom::Coverage::D3::COLOR_IGNORE = T.let(T.unsafe(nil), String)
628
634
  Spoom::Coverage::D3::COLOR_STRICT = T.let(T.unsafe(nil), String)
@@ -672,27 +678,32 @@ class Spoom::Coverage::D3::ColorPalette
672
678
  sig { returns(::String) }
673
679
  def false_color; end
674
680
 
675
- def false_color=(_arg0); end
681
+ sig { params(false_color: ::String).returns(::String) }
682
+ def false_color=(false_color); end
676
683
 
677
684
  sig { returns(::String) }
678
685
  def ignore_color; end
679
686
 
680
- def ignore_color=(_arg0); end
687
+ sig { params(ignore_color: ::String).returns(::String) }
688
+ def ignore_color=(ignore_color); end
681
689
 
682
690
  sig { returns(::String) }
683
691
  def strict_color; end
684
692
 
685
- def strict_color=(_arg0); end
693
+ sig { params(strict_color: ::String).returns(::String) }
694
+ def strict_color=(strict_color); end
686
695
 
687
696
  sig { returns(::String) }
688
697
  def strong_color; end
689
698
 
690
- def strong_color=(_arg0); end
699
+ sig { params(strong_color: ::String).returns(::String) }
700
+ def strong_color=(strong_color); end
691
701
 
692
702
  sig { returns(::String) }
693
703
  def true_color; end
694
704
 
695
- def true_color=(_arg0); end
705
+ sig { params(true_color: ::String).returns(::String) }
706
+ def true_color=(true_color); end
696
707
  end
697
708
 
698
709
  class Spoom::Coverage::D3::Pie < ::Spoom::Coverage::D3::Base
@@ -939,62 +950,74 @@ class Spoom::Coverage::Snapshot
939
950
  sig { returns(::Integer) }
940
951
  def calls_typed; end
941
952
 
942
- def calls_typed=(_arg0); end
953
+ sig { params(calls_typed: ::Integer).returns(::Integer) }
954
+ def calls_typed=(calls_typed); end
943
955
 
944
956
  sig { returns(::Integer) }
945
957
  def calls_untyped; end
946
958
 
947
- def calls_untyped=(_arg0); end
959
+ sig { params(calls_untyped: ::Integer).returns(::Integer) }
960
+ def calls_untyped=(calls_untyped); end
948
961
 
949
962
  sig { returns(::Integer) }
950
963
  def classes; end
951
964
 
952
- def classes=(_arg0); end
965
+ sig { params(classes: ::Integer).returns(::Integer) }
966
+ def classes=(classes); end
953
967
 
954
968
  sig { returns(T.nilable(::String)) }
955
969
  def commit_sha; end
956
970
 
957
- def commit_sha=(_arg0); end
971
+ sig { params(commit_sha: T.nilable(::String)).returns(T.nilable(::String)) }
972
+ def commit_sha=(commit_sha); end
958
973
 
959
974
  sig { returns(T.nilable(::Integer)) }
960
975
  def commit_timestamp; end
961
976
 
962
- def commit_timestamp=(_arg0); end
977
+ sig { params(commit_timestamp: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
978
+ def commit_timestamp=(commit_timestamp); end
963
979
 
964
980
  sig { returns(::Integer) }
965
981
  def duration; end
966
982
 
967
- def duration=(_arg0); end
983
+ sig { params(duration: ::Integer).returns(::Integer) }
984
+ def duration=(duration); end
968
985
 
969
986
  sig { returns(::Integer) }
970
987
  def files; end
971
988
 
972
- def files=(_arg0); end
989
+ sig { params(files: ::Integer).returns(::Integer) }
990
+ def files=(files); end
973
991
 
974
992
  sig { returns(::Integer) }
975
993
  def methods_with_sig; end
976
994
 
977
- def methods_with_sig=(_arg0); end
995
+ sig { params(methods_with_sig: ::Integer).returns(::Integer) }
996
+ def methods_with_sig=(methods_with_sig); end
978
997
 
979
998
  sig { returns(::Integer) }
980
999
  def methods_with_sig_excluding_rbis; end
981
1000
 
982
- def methods_with_sig_excluding_rbis=(_arg0); end
1001
+ sig { params(methods_with_sig_excluding_rbis: ::Integer).returns(::Integer) }
1002
+ def methods_with_sig_excluding_rbis=(methods_with_sig_excluding_rbis); end
983
1003
 
984
1004
  sig { returns(::Integer) }
985
1005
  def methods_without_sig; end
986
1006
 
987
- def methods_without_sig=(_arg0); end
1007
+ sig { params(methods_without_sig: ::Integer).returns(::Integer) }
1008
+ def methods_without_sig=(methods_without_sig); end
988
1009
 
989
1010
  sig { returns(::Integer) }
990
1011
  def methods_without_sig_excluding_rbis; end
991
1012
 
992
- def methods_without_sig_excluding_rbis=(_arg0); end
1013
+ sig { params(methods_without_sig_excluding_rbis: ::Integer).returns(::Integer) }
1014
+ def methods_without_sig_excluding_rbis=(methods_without_sig_excluding_rbis); end
993
1015
 
994
1016
  sig { returns(::Integer) }
995
1017
  def modules; end
996
1018
 
997
- def modules=(_arg0); end
1019
+ sig { params(modules: ::Integer).returns(::Integer) }
1020
+ def modules=(modules); end
998
1021
 
999
1022
  sig { params(out: T.any(::IO, ::StringIO), colors: T::Boolean, indent_level: ::Integer).void }
1000
1023
  def print(out: T.unsafe(nil), colors: T.unsafe(nil), indent_level: T.unsafe(nil)); end
@@ -1002,27 +1025,32 @@ class Spoom::Coverage::Snapshot
1002
1025
  sig { returns(::Integer) }
1003
1026
  def rbi_files; end
1004
1027
 
1005
- def rbi_files=(_arg0); end
1028
+ sig { params(rbi_files: ::Integer).returns(::Integer) }
1029
+ def rbi_files=(rbi_files); end
1006
1030
 
1007
1031
  sig { returns(T::Hash[::String, ::Integer]) }
1008
1032
  def sigils; end
1009
1033
 
1010
- def sigils=(_arg0); end
1034
+ sig { params(sigils: T::Hash[::String, ::Integer]).returns(T::Hash[::String, ::Integer]) }
1035
+ def sigils=(sigils); end
1011
1036
 
1012
1037
  sig { returns(T::Hash[::String, ::Integer]) }
1013
1038
  def sigils_excluding_rbis; end
1014
1039
 
1015
- def sigils_excluding_rbis=(_arg0); end
1040
+ sig { params(sigils_excluding_rbis: T::Hash[::String, ::Integer]).returns(T::Hash[::String, ::Integer]) }
1041
+ def sigils_excluding_rbis=(sigils_excluding_rbis); end
1016
1042
 
1017
1043
  sig { returns(::Integer) }
1018
1044
  def singleton_classes; end
1019
1045
 
1020
- def singleton_classes=(_arg0); end
1046
+ sig { params(singleton_classes: ::Integer).returns(::Integer) }
1047
+ def singleton_classes=(singleton_classes); end
1021
1048
 
1022
1049
  sig { returns(::Integer) }
1023
1050
  def timestamp; end
1024
1051
 
1025
- def timestamp=(_arg0); end
1052
+ sig { params(timestamp: ::Integer).returns(::Integer) }
1053
+ def timestamp=(timestamp); end
1026
1054
 
1027
1055
  sig { returns(T::Hash[::String, T.untyped]) }
1028
1056
  def to_h; end
@@ -1033,12 +1061,14 @@ class Spoom::Coverage::Snapshot
1033
1061
  sig { returns(T.nilable(::String)) }
1034
1062
  def version_runtime; end
1035
1063
 
1036
- def version_runtime=(_arg0); end
1064
+ sig { params(version_runtime: T.nilable(::String)).returns(T.nilable(::String)) }
1065
+ def version_runtime=(version_runtime); end
1037
1066
 
1038
1067
  sig { returns(T.nilable(::String)) }
1039
1068
  def version_static; end
1040
1069
 
1041
- def version_static=(_arg0); end
1070
+ sig { params(version_static: T.nilable(::String)).returns(T.nilable(::String)) }
1071
+ def version_static=(version_static); end
1042
1072
 
1043
1073
  class << self
1044
1074
  sig { params(json: ::String).returns(::Spoom::Coverage::Snapshot) }
@@ -1046,9 +1076,15 @@ class Spoom::Coverage::Snapshot
1046
1076
 
1047
1077
  sig { params(obj: T::Hash[::String, T.untyped]).returns(::Spoom::Coverage::Snapshot) }
1048
1078
  def from_obj(obj); end
1079
+
1080
+ private
1081
+
1082
+ sig { params(obj: T::Hash[::String, T.untyped], key: ::String, default: ::Integer).returns(::Integer) }
1083
+ def integer_field(obj, key, default: T.unsafe(nil)); end
1049
1084
  end
1050
1085
  end
1051
1086
 
1087
+ class Spoom::Coverage::Snapshot::Error < ::Spoom::Error; end
1052
1088
  Spoom::Coverage::Snapshot::STRICTNESSES = T.let(T.unsafe(nil), Array)
1053
1089
 
1054
1090
  class Spoom::Coverage::SnapshotPrinter < ::Spoom::Printer
@@ -1577,7 +1613,8 @@ class Spoom::Deadcode::Remover::NodeContext
1577
1613
  sig { returns(T::Array[::Prism::Node]) }
1578
1614
  def nesting; end
1579
1615
 
1580
- def nesting=(_arg0); end
1616
+ sig { params(nesting: T::Array[::Prism::Node]).returns(T::Array[::Prism::Node]) }
1617
+ def nesting=(nesting); end
1581
1618
 
1582
1619
  sig { returns(T.nilable(::Prism::Node)) }
1583
1620
  def next_node; end
@@ -2322,7 +2359,8 @@ class Spoom::LSP::SymbolPrinter < ::Spoom::Printer
2322
2359
  sig { returns(T.nilable(::String)) }
2323
2360
  def prefix; end
2324
2361
 
2325
- def prefix=(_arg0); end
2362
+ sig { params(prefix: T.nilable(::String)).returns(T.nilable(::String)) }
2363
+ def prefix=(prefix); end
2326
2364
 
2327
2365
  sig { params(objects: T::Array[::Spoom::LSP::PrintableSymbol]).void }
2328
2366
  def print_list(objects); end
@@ -2482,7 +2520,8 @@ class Spoom::Model::Class < ::Spoom::Model::Namespace
2482
2520
  sig { returns(T.nilable(::String)) }
2483
2521
  def superclass_name; end
2484
2522
 
2485
- def superclass_name=(_arg0); end
2523
+ sig { params(superclass_name: T.nilable(::String)).returns(T.nilable(::String)) }
2524
+ def superclass_name=(superclass_name); end
2486
2525
  end
2487
2526
 
2488
2527
  class Spoom::Model::Comment
@@ -2879,7 +2918,8 @@ class Spoom::Printer
2879
2918
  sig { returns(T.any(::IO, ::StringIO)) }
2880
2919
  def out; end
2881
2920
 
2882
- def out=(_arg0); end
2921
+ sig { params(out: T.any(::IO, ::StringIO)).returns(T.any(::IO, ::StringIO)) }
2922
+ def out=(out); end
2883
2923
 
2884
2924
  sig { params(string: T.nilable(::String)).void }
2885
2925
  def print(string); end
@@ -2974,7 +3014,8 @@ class Spoom::Sorbet::Config
2974
3014
  sig { returns(T::Boolean) }
2975
3015
  def no_stdlib; end
2976
3016
 
2977
- def no_stdlib=(_arg0); end
3017
+ sig { params(no_stdlib: T::Boolean).returns(T::Boolean) }
3018
+ def no_stdlib=(no_stdlib); end
2978
3019
 
2979
3020
  sig { returns(::String) }
2980
3021
  def options_string; end
@@ -2982,7 +3023,9 @@ class Spoom::Sorbet::Config
2982
3023
  sig { returns(T::Array[::String]) }
2983
3024
  def paths; end
2984
3025
 
2985
- def paths=(_arg0); end
3026
+ sig { params(paths: T::Array[::String]).returns(T::Array[::String]) }
3027
+ def paths=(paths); end
3028
+
2986
3029
  def typed_overrides; end
2987
3030
  def typed_overrides=(_arg0); end
2988
3031
 
@@ -3216,6 +3259,9 @@ module Spoom::Sorbet::Sigils
3216
3259
  sig { params(path: T.any(::Pathname, ::String)).returns(T.nilable(::String)) }
3217
3260
  def file_strictness(path); end
3218
3261
 
3262
+ sig { params(content: ::String, strictness: ::String).returns(T::Boolean) }
3263
+ def satisfies_minimum_strictness?(content, strictness); end
3264
+
3219
3265
  sig { params(strictness: ::String).returns(::String) }
3220
3266
  def sigil_string(strictness); end
3221
3267
 
@@ -3322,6 +3368,9 @@ end
3322
3368
 
3323
3369
  module Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs
3324
3370
  class << self
3371
+ sig { params(ruby_contents: ::String).returns(T::Boolean) }
3372
+ def contains_rbs_syntax?(ruby_contents); end
3373
+
3325
3374
  sig do
3326
3375
  params(
3327
3376
  ruby_contents: ::String,
data/templates/page.erb CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta http-equiv="x-ua-compatible" content="ie=edge" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
7
 
8
- <title><%= title %></title>
8
+ <title><%= ERB::Util.html_escape(title) %></title>
9
9
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
10
10
 
11
11
  <style>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.10
4
+ version: 1.8.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa