squoosh 0.2.1 → 0.4.0

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: 65d794e4fa97573f5ffe9fbc8339df1fcd8ef91908d743be565345a0e5a7b92f
4
- data.tar.gz: 857fb11296b5e4a3989a06d660e44a4c6536780f21d85092e2ab5b11f82bcceb
3
+ metadata.gz: 5629b74c30885ffe74552245300726730050c5b08be3a0ae3270d87d26940b3b
4
+ data.tar.gz: e10c8aa994b60e7ad192da3b0c0f5ef27346827c7e777f30a5b828d5c891a718
5
5
  SHA512:
6
- metadata.gz: 6487482bb47b8c43b7215ca3d901128fe39abc33044c7ac9cd0d8b2b9f09ac90f9b9ff1d7750bea6ea907b31c4044b0c05e19231871bb9306cb54b508de0dd34
7
- data.tar.gz: 3693641100489cc0128c5f0569eca44607ddac2c126b265b0a837653b6e847e04887034dea3a648049a3841e8d6e368f9d628e375b16eb33c22402328c42d0b2
6
+ metadata.gz: 64cd4ba5c5c8ab4de943685546a0aef694489c1f68ab809383b21f2a1bc650e68b0c20d5a4eeafaa144a44e9f35978c44868b1bd27c857fa41442a26cae48db2
7
+ data.tar.gz: d3d0e28e5b4201552f7db683c97b6b5b7195dae718abf020869b180b23c6eab04b07ed5bc501310b88d8a4ba481247b78149ee45b31eb93e045cce215713b2dc
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016, 2018 Stephen Checkoway
3
+ Copyright (c) 2016, 2018, 2019 Stephen Checkoway
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
+ [![Actions
2
+ Status](https://github.com/stevecheckoway/squoosh/workflows/CI%20Test/badge.svg)](https://github.com/stevecheckoway/squoosh/actions)
3
+ [![Coverage
4
+ Status](https://coveralls.io/repos/github/stevecheckoway/squoosh/badge.svg?branch=master)](https://coveralls.io/github/stevecheckoway/squoosh?branch=master)
5
+
1
6
  # Squoosh
2
7
 
3
8
  Minifies HTML, JavaScript, and CSS, including inline JavaScript and CSS.
4
9
 
5
- [![Travis-CI Build
6
- Status](https://travis-ci.org/stevecheckoway/squoosh.svg)](https://travis-ci.org/stevecheckoway/squoosh)
7
-
8
- CSS minification is handled by [Sass](http://www.rubydoc.info/gems/sass)
10
+ CSS minification is handled by [Sassc](http://www.rubydoc.info/gems/sassc)
9
11
  whereas JavaScript minification is handled by
10
12
  [Uglifier](http://www.rubydoc.info/gems/uglifier) which requires node.js.
11
13
 
@@ -17,7 +19,7 @@ nodes](https://html.spec.whatwg.org/multipage/dom.html#inter-element-whitespace)
17
19
  are removed from the DOM and semantically meaningfull runs of whitespace are
18
20
  compressed to single spaces, except in `pre`, `textarea`, and
19
21
  [foreign](https://html.spec.whatwg.org/multipage/syntax.html#elements-2) elements.
20
- Then, inline JavaScript and CSS are compressed using Sass and Uglifier.
22
+ Then, inline JavaScript and CSS are compressed using Sassc and Uglifier.
21
23
  Finally, the DOM is serialized, compressing
22
24
  [attributes](https://html.spec.whatwg.org/multipage/syntax.html#attributes-2)
23
25
  where possible and omitting [optional start and end
@@ -32,8 +34,17 @@ Squoosh will not minify
32
34
 
33
35
  - HTML 4 and earlier;
34
36
  - XHTML, any version;
35
- - [MathML](https://www.w3.org/TR/MathML3/) elements; nor
36
- - [SVG](https://www.w3.org/TR/SVG11/) elements.
37
+ - spaces in [MathML](https://www.w3.org/TR/MathML3/) elements; nor
38
+ - spaces in [SVG](https://www.w3.org/TR/SVG11/) elements.
39
+
40
+ HTML 4 and XHTML documents (more precisely, any document that does not have an
41
+ HTML 5 DOCTYPE, typically `<!DOCTYPE html>`) is returned unchanged by
42
+ `Squoosh::minify_html`.
43
+
44
+ MathML and SVG elements have their tags minified (including compressing
45
+ attributes and serializing empty elements as self-closing start tags) and
46
+ comments inside them are removed. White space is preserved (except for the
47
+ newline normalization performed by the HTML 5 parser).
37
48
 
38
49
  ## Installation
39
50
 
@@ -54,7 +65,7 @@ Or install it yourself as:
54
65
  ## Usage
55
66
 
56
67
  You can read the documentation
57
- [here](https://www.rubydoc.info/github/stevecheckoway/squoosh/v0.2.0).
68
+ [here](https://www.rubydoc.info/gems/squoosh/).
58
69
 
59
70
  The three basic minification functions are
60
71
 
@@ -70,14 +81,20 @@ significantly speed up minifying HTML with repeated scripts and style sheets.
70
81
  Create a `_plugins/squoosh.rb` file with the contents
71
82
 
72
83
  ```ruby
73
- require 'squoosh'
74
-
75
- Jekyll::Hooks.register [:documents, :pages], :post_render, priority: :high do |doc|
76
- case File.extname(doc.destination('./'))
77
- when '.html', '.htm'
78
- doc.output = Squoosh::minify_html doc.output
79
- when '.js'
80
- doc.output = Squoosh::minify_js doc.output
84
+ # frozen_string_literal: true
85
+
86
+ if Jekyll.env == 'deploy'
87
+ require 'squoosh'
88
+
89
+ squoosher = Squoosh::Squoosher.new
90
+ Jekyll::Hooks.register(%i[documents pages],
91
+ :post_render, priority: :high) do |doc|
92
+ case File.extname(doc.destination('./'))
93
+ when '.html', '.htm'
94
+ doc.output = squoosher.minify_html(doc.output)
95
+ when '.js'
96
+ doc.output = squoosher.minify_js(doc.output)
97
+ end
81
98
  end
82
99
  end
83
100
  ```
data/lib/squoosh.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'squoosh/version'
4
- require 'nokogumbo'
4
+ require 'nokogiri'
5
5
  require 'sassc'
6
6
  require 'set'
7
7
  require 'uglifier'
@@ -74,6 +74,7 @@ module Squoosh
74
74
  end
75
75
  @options = DEFAULT_OPTIONS.merge(options)
76
76
  @js_cache = {}
77
+ @inline_script_cache = {}
77
78
  @css_cache = {}
78
79
  end
79
80
 
@@ -95,7 +96,7 @@ module Squoosh
95
96
  doc.children.map { |node| stringify_node(node) }.join
96
97
  end
97
98
 
98
- # Minify CSS using Sass.
99
+ # Minify CSS using Sassc.
99
100
  #
100
101
  # @param content [String] the CSS to minify
101
102
  # @return [String] the minified CSS
@@ -111,8 +112,7 @@ module Squoosh
111
112
  # @param content [String] the JavaScript to minify
112
113
  # @return [String] the minified JavaScript
113
114
  def minify_js(content)
114
- @js_cache[content] ||= \
115
- Uglifier.compile(content, @options[:uglifier_options])
115
+ @js_cache[content] ||= uglify(content, @options[:uglifier_options])
116
116
  end
117
117
 
118
118
  # Element kinds
@@ -124,6 +124,13 @@ module Squoosh
124
124
  private_constant :VOID_ELEMENTS, :RAW_TEXT_ELEMENTS
125
125
  private_constant :ESCAPABLE_RAW_TEXT_ELEMENTS, :FOREIGN_ELEMENTS
126
126
 
127
+ INLINE_SCRIPT_OPTIONS = {
128
+ output: {
129
+ inline_script: true
130
+ }
131
+ }.freeze
132
+ private_constant :INLINE_SCRIPT_OPTIONS
133
+
127
134
  private
128
135
 
129
136
  def void_element?(node)
@@ -197,6 +204,7 @@ module Squoosh
197
204
  false
198
205
  end
199
206
 
207
+ # rubocop:disable Style/StringConcatenation
200
208
  EVENT_HANDLERS_XPATH = (
201
209
  # Select all attribute nodes whose names start with "on";
202
210
  '//@*[starts-with(name(),"on")]' +
@@ -224,17 +232,31 @@ module Squoosh
224
232
  rejectionhandled storage unhandledrejection
225
233
  unload].map { |n| "name()=\"on#{n}\"" }.join(' or ') +
226
234
  ')' \
227
- ']'
235
+ ']'
228
236
  ).freeze
237
+ # rubocop:enable Style/StringConcatenation
229
238
  private_constant :EVENT_HANDLERS_XPATH
230
239
 
240
+ def uglify(content, options)
241
+ js = Uglifier.compile(content, options)
242
+ js.chomp!(';')
243
+ js
244
+ end
245
+
246
+ def compress_script(content)
247
+ @inline_script_cache[content] ||= begin
248
+ options = @options[:uglifier_options].merge(INLINE_SCRIPT_OPTIONS)
249
+ uglify(content, options)
250
+ end
251
+ end
252
+
231
253
  def compress_javascript(doc)
232
254
  # Compress script elements.
233
255
  doc.xpath('//script[not(ancestor::math or ancestor::svg)]').each do |node|
234
256
  type = node['type']&.downcase
235
257
  next unless type.nil? || type == 'text/javascript'
236
258
 
237
- node.content = minify_js node.content
259
+ node.content = compress_script(node.content)
238
260
  end
239
261
  # Compress event handlers.
240
262
  doc.xpath(EVENT_HANDLERS_XPATH).each do |attr|
@@ -272,15 +294,7 @@ module Squoosh
272
294
  end
273
295
  elsif node.element? &&
274
296
  (node.name == 'pre' || node.name == 'textarea')
275
- # Remove leading newline in pre and textarea tags unless there are two
276
- # in a row.
277
- if node.children[0]&.text?
278
- content = node.children[0].content
279
- if content.sub!(/\A\r\n?([^\r]|\z)/, '\1') ||
280
- content.sub!(/\A\n([^\n]|\z)/, '\1')
281
- node.children[0].content = content
282
- end
283
- end
297
+ # Leave the contents of these nodes alone.
284
298
  elsif normal_element?(node) || node.name == 'title'
285
299
  # Compress spaces in normal elements and title.
286
300
  node.children.each { |c| compress_spaces c }
@@ -323,7 +337,8 @@ module Squoosh
323
337
 
324
338
  # Add attributes. 8.1.2.3
325
339
  last_attr_unquoted = false
326
- node.attributes.each do |name, attr|
340
+ node.attribute_nodes.each do |attr|
341
+ name = qualified_attribute_name(attr)
327
342
  last_attr_unquoted = false
328
343
  # Make sure there are no character references.
329
344
  # XXX: We should be able to compress a bit more by leaving bare & in
@@ -332,7 +347,7 @@ module Squoosh
332
347
  # value.gsub!(/&([a-zA-Z0-9]+;|#[0-9]+|#[xX][a-fA-F0-9]+)/, '&amp;\1')
333
348
  value = (attr.value || '').gsub('&', '&amp;')
334
349
  if value.empty?
335
- output << ' ' + name
350
+ output << " #{name}"
336
351
  elsif /[\t\n\f\r "'`=<>]/ !~ value
337
352
  last_attr_unquoted = true
338
353
  output << " #{name}=#{value}"
@@ -347,13 +362,23 @@ module Squoosh
347
362
  end
348
363
 
349
364
  # Close start tag.
350
- if node_is_self_closing? node
365
+ if self_closing? node
351
366
  output << ' ' if last_attr_unquoted
352
367
  output << '/'
353
368
  end
354
369
  output << '>'
355
370
  end
356
371
 
372
+ # If pre or textarea start with a newline, double it because the HTML
373
+ # parser strips leading newlines.
374
+ if (node.name == 'pre' || node.name == 'textarea') &&
375
+ !node.children.empty?
376
+ first_child = node.children[0]
377
+ if first_child.text? && first_child.content.start_with?("\n")
378
+ output << "\n"
379
+ end
380
+ end
381
+
357
382
  # Add content.
358
383
  output << node.children.map { |c| stringify_node c }.join
359
384
 
@@ -362,10 +387,81 @@ module Squoosh
362
387
  output.string
363
388
  end
364
389
 
365
- def node_is_self_closing?(node)
390
+ def qualified_attribute_name(attr)
391
+ ns = attr.namespace
392
+ return attr.name if ns.nil?
393
+
394
+ uri = ns.href
395
+ if uri == Nokogiri::HTML5::XML_NAMESPACE
396
+ "xml:#{attr.name}"
397
+ elsif uri == Nokogiri::HTML5::XMLNS_NAMESPACE && attr.name == 'xmlns'
398
+ 'xmlns'
399
+ elsif uri == Nokogiri::HTML5::XMLNS_NAMESPACE
400
+ "xmlns:#{attr.name}"
401
+ elsif uri == Nokogiri::HTML5::XLINK_NAMESPACE
402
+ "xlink:#{attr.name}"
403
+ else
404
+ # :nocov:
405
+ raise 'Unreachable!'
406
+ # :nocov:
407
+ end
408
+ end
409
+
410
+ def self_closing?(node)
411
+ # If we're not omitting end tags, then don't mark foreign elements as
412
+ # self closing.
413
+ return false unless @options[:omit_tags]
414
+
366
415
  foreign_element?(node) && node.children.empty?
367
416
  end
368
417
 
418
+ def content_node?(node)
419
+ # Inter-element whitespace, comment nodes, and processing instruction
420
+ # nodes must be ignored when establishing whether an element's contents
421
+ # match the element's content model or not, and must be ignored when
422
+ # following algorithms that define document and element semantics.
423
+ !(node.comment? ||
424
+ node.processing_instruction? ||
425
+ inter_element_whitespace?(node))
426
+ end
427
+
428
+ def previous_sibling_content_node(node)
429
+ while (node = node.previous_sibling)
430
+ return node if content_node?(node)
431
+ end
432
+ nil
433
+ end
434
+
435
+ def next_sibling_content_node(node)
436
+ while (node = node.next_sibling)
437
+ return node if content_node?(node)
438
+ end
439
+ nil
440
+ end
441
+
442
+ def first_child_content_node(node)
443
+ return nil if node.children.empty?
444
+
445
+ node = node.children[0]
446
+ return node if content_node?(node)
447
+
448
+ next_sibling_content_node(node)
449
+ end
450
+
451
+ def next_sibling_is_nil_or_one_of?(node, elements)
452
+ node = next_sibling_content_node(node)
453
+ node.nil? || (node.element? && elements.include?(node.name))
454
+ end
455
+
456
+ def next_sibling_is_one_of?(node, elements)
457
+ node = next_sibling_content_node(node)
458
+ node&.element? && elements.include?(node.name)
459
+ end
460
+
461
+ def parent_contains_more_content?(node)
462
+ !next_sibling_content_node(node).nil?
463
+ end
464
+
369
465
  def omit_start_tag?(node)
370
466
  return false unless @options[:omit_tags]
371
467
  return false unless node.attributes.empty?
@@ -400,48 +496,36 @@ module Squoosh
400
496
  # inside the colgroup element is a col element, and if the element is
401
497
  # not immediately preceded by another colgroup element whose end tag
402
498
  # has been omitted. (It can't be omitted if the element is empty.)
403
- return false if node.children.empty?
404
- return false unless node.children[0].name == 'col'
499
+ child = first_child_content_node(node)
500
+ return false if child.nil? || !child.element? || child.name != 'col'
405
501
 
406
- prev_elm = node.previous_element
407
- return prev_elm.nil? ||
408
- prev_elm.name != 'col' ||
409
- !omit_end_tag?(prev_elm)
502
+ prev_node = previous_sibling_content_node(node)
503
+ return !(prev_node&.element? &&
504
+ prev_node.name == 'colgroup' &&
505
+ omit_end_tag?(prev_node))
410
506
 
411
507
  when 'tbody'
412
508
  # A tbody element's start tag may be omitted if the first thing inside
413
509
  # the tbody element is a tr element, and if the element is not
414
510
  # immediately preceded by a tbody, thead, or tfoot element whose end
415
511
  # tag has been omitted. (It can't be omitted if the element is empty.)
416
- return false if node.children.empty?
417
- return false unless node.children[0].name == 'tr'
512
+ child = first_child_content_node(node)
513
+ return false if child.nil? || !child.element? || child.name != 'tr'
418
514
 
419
- prev_elm = node.previous_element
420
- return prev_elm.nil? ||
421
- !%w[tbody thead tfoot].include?(prev_elm.name) ||
422
- !omit_end_tag?(prev_elm)
423
- end
424
- false
425
- end
426
-
427
- def parent_contains_more_content?(node)
428
- while (node = node.next_sibling)
429
- next if node.comment?
430
- next if node.processing_instruction?
431
- next if inter_element_whitespace? node
432
-
433
- return true
515
+ prev_node = previous_sibling_content_node(node)
516
+ return !(prev_node&.element? &&
517
+ %w[tbody thead tfoot].include?(prev_node.name) &&
518
+ omit_end_tag?(prev_node))
434
519
  end
435
520
  false
436
521
  end
437
522
 
438
523
  def omit_end_tag?(node)
439
- return true if void_element? node
524
+ return true if void_element?(node) || self_closing?(node)
440
525
  return false unless @options[:omit_tags]
441
526
  return false if node.parent.name == 'noscript'
442
527
 
443
528
  next_node = node.next_sibling
444
- next_elm = node.next_element
445
529
  case node.name
446
530
  when 'html'
447
531
  # An html element's end tag may be omitted if the html element is not
@@ -464,33 +548,40 @@ module Squoosh
464
548
  # An li element's end tag may be omitted if the li element is
465
549
  # immediately followed by another li element or if there is no more
466
550
  # content in the parent element.
467
- return next_elm&.name == 'li' || !parent_contains_more_content?(node)
551
+ return next_sibling_is_nil_or_one_of?(node, ['li'])
468
552
 
469
553
  when 'dt'
470
554
  # A dt element's end tag may be omitted if the dt element is immediately
471
555
  # followed by another dt element or a dd element.
472
- return %w[dt dd].include? next_elm&.name
556
+ return next_sibling_is_one_of?(node, %w[dt dd])
473
557
 
474
558
  when 'dd'
475
559
  # A dd element's end tag may be omitted if the dd element is immediately
476
560
  # followed by another dd element or a dt element, or if there is no more
477
561
  # content in the parent element.
478
- return %w[dt dd].include?(next_elm&.name) ||
479
- !parent_contains_more_content?(node)
562
+ return next_sibling_is_nil_or_one_of?(node, %w[dt dd])
480
563
 
481
564
  when 'p'
482
- # A p element's end tag may be omitted if the p element is immediately
483
- # followed by an address, article, aside, blockquote, div, dl,
484
- # fieldset, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, hr,
485
- # main, nav, ol, p, pre, section, table, or ul, element, or if there
486
- # is no more content in the parent element and the parent element is
487
- # not an a element.
488
- return true if %w[address article aside blockquote div dl
489
- fieldset footer form h1 h2 h3 h4
490
- h5 h6 header hgroup hr main nav ol
491
- p pre section table ul].include? next_elm&.name
492
-
493
- return node.parent.name != 'a' && !parent_contains_more_content?(node)
565
+ # A p element's end tag can be omitted if the p element is immediately
566
+ # followed by an address, article, aside, blockquote, details, div,
567
+ # dl, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5,
568
+ # h6, header, hgroup, hr, main, menu, nav, ol, p, pre, section, table,
569
+ # or ul element, or if there is no more content in the parent element
570
+ # and the parent element is an HTML element that is not an a, audio,
571
+ # del, ins, map, noscript, or video element, or an autonomous custom
572
+ # element.
573
+ return true if next_sibling_is_one_of?(
574
+ node,
575
+ %w[
576
+ address article aside blockquote details div dl fieldset figcaption
577
+ figure footer form h1 h2 h3 h4 h5 h6 header hgroup hr main menu nav
578
+ ol p pre section table ul
579
+ ]
580
+ )
581
+ return false if foreign_element?(node.parent)
582
+
583
+ return !parent_contains_more_content?(node) &&
584
+ !%(a audio del ins map noscript video).include?(node.parent.name)
494
585
 
495
586
  when 'rb', 'rt', 'rp'
496
587
  # An rb element's end tag may be omitted if the rb element is
@@ -504,62 +595,56 @@ module Squoosh
504
595
  # An rp element's end tag may be omitted if the rp element is
505
596
  # immediately followed by an rb, rt, rtc or rp element, or if there is
506
597
  # no more content in the parent element.
507
- return %w[rb rt rtc rp].include?(next_elm&.name) ||
508
- !parent_contains_more_content?(node)
598
+ return next_sibling_is_nil_or_one_of?(node, %w[rb rt rtc rp])
599
+
509
600
  when 'rtc'
510
601
  # An rtc element's end tag may be omitted if the rtc element is
511
602
  # immediately followed by an rb, rtc or rp element, or if there is no
512
603
  # more content in the parent element.
513
- return %w[rb rtc rp].include?(next_elm&.name) ||
514
- !parent_contains_more_content?(node)
604
+ return next_sibling_is_nil_or_one_of?(node, %w[rb rtc rp])
515
605
 
516
606
  when 'optgroup'
517
607
  # An optgroup element's end tag may be omitted if the optgroup element
518
608
  # is immediately followed by another optgroup element, or if there is
519
609
  # no more content in the parent element.
520
- return next_elm&.name == 'optgroup' ||
521
- !parent_contains_more_content?(node)
610
+ return next_sibling_is_nil_or_one_of?(node, ['optgroup'])
522
611
 
523
612
  when 'option'
524
613
  # An option element's end tag may be omitted if the option element is
525
614
  # immediately followed by another option element, or if it is
526
615
  # immediately followed by an optgroup element, or if there is no more
527
616
  # content in the parent element.
528
- return %w[option optgroup].include?(next_elm&.name) ||
529
- !parent_contains_more_content?(node)
617
+ return next_sibling_is_nil_or_one_of?(node, %w[option optgroup])
530
618
 
531
619
  when 'colgroup'
532
620
  # A colgroup element's end tag may be omitted if the colgroup element is
533
621
  # not immediately followed by a space character or a comment.
534
- return next_node.nil? ||
535
- (next_node.text? && !next_node.content.start_with(' ')) ||
536
- !next_node.comment?
622
+ return true if next_node.nil?
623
+ return !next_node.content.start_with?(' ') if next_node.text?
624
+
625
+ return !next_node.comment?
537
626
 
538
627
  when 'thead'
539
628
  # A thead element's end tag may be omitted if the thead element is
540
629
  # immediately followed by a tbody or tfoot element.
541
- return %w[tbody tfoot].include? next_elm&.name
630
+ return next_sibling_is_one_of?(node, %w[tbody tfoot])
542
631
 
543
632
  when 'tbody'
544
633
  # A tbody element's end tag may be omitted if the tbody element is
545
634
  # immediately followed by a tbody or tfoot element, or if there is no
546
635
  # more content in the parent element.
547
- return %w[tbody tfoot].include?(next_elm&.name) ||
548
- !parent_contains_more_content?(node)
636
+ return next_sibling_is_nil_or_one_of?(node, %w[tbody tfoot])
549
637
 
550
638
  when 'tfoot'
551
- # A tfoot element's end tag may be omitted if the tfoot element is
552
- # immediately followed by a tbody element, or if there is no more
553
- # content in the parent element.
554
- return next_elm&.name == 'tbody' ||
555
- !parent_contains_more_content?(node)
639
+ # A tfoot element's end tag can be omitted if there is no more content
640
+ # in the parent element.
641
+ return !parent_contains_more_content?(node)
556
642
 
557
643
  when 'tr'
558
644
  # A tr element's end tag may be omitted if the tr element is immediately
559
645
  # followed by another tr element, or if there is no more content in the
560
646
  # parent element.
561
- return next_elm&.name == 'tr' ||
562
- !parent_contains_more_content?(node)
647
+ return next_sibling_is_nil_or_one_of?(node, ['tr'])
563
648
 
564
649
  when 'td', 'th'
565
650
  # A td element's end tag may be omitted if the td element is immediately
@@ -569,8 +654,7 @@ module Squoosh
569
654
  # A th element's end tag may be omitted if the th element is immediately
570
655
  # followed by a td or th element, or if there is no more content in the
571
656
  # parent element.
572
- return %w[td th].include?(next_elm&.name) ||
573
- !parent_contains_more_content?(node)
657
+ return next_sibling_is_nil_or_one_of?(node, %w[td th])
574
658
  end
575
659
  false
576
660
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Squoosh
4
4
  # The version of squoosh.
5
- VERSION = '0.2.1'
5
+ VERSION = '0.4.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squoosh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Checkoway
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-28 00:00:00.000000000 Z
11
+ date: 2021-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov-lcov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -95,33 +109,33 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: nokogumbo
112
+ name: nokogiri
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '2.0'
117
+ version: '1.12'
104
118
  type: :runtime
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '2.0'
124
+ version: '1.12'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: sassc
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '2.0'
131
+ version: '2.1'
118
132
  type: :runtime
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '2.0'
138
+ version: '2.1'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: uglifier
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +150,7 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '4.1'
139
- description:
153
+ description:
140
154
  email:
141
155
  - s@pahtak.org
142
156
  executables: []
@@ -155,23 +169,26 @@ metadata:
155
169
  changelog_uri: https://github.com/stevecheckoway/squoosh/blob/master/CHANGELOG.md
156
170
  homepage_uri: https://github.com/stevecheckoway/squoosh
157
171
  source_code_uri: https://github.com/stevecheckoway/squoosh
158
- post_install_message:
172
+ post_install_message:
159
173
  rdoc_options: []
160
174
  require_paths:
161
175
  - lib
162
176
  required_ruby_version: !ruby/object:Gem::Requirement
163
177
  requirements:
164
- - - "~>"
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '2.6'
181
+ - - "<"
165
182
  - !ruby/object:Gem::Version
166
- version: '2.3'
183
+ version: '4.0'
167
184
  required_rubygems_version: !ruby/object:Gem::Requirement
168
185
  requirements:
169
186
  - - ">="
170
187
  - !ruby/object:Gem::Version
171
188
  version: '0'
172
189
  requirements: []
173
- rubygems_version: 3.0.3
174
- signing_key:
190
+ rubygems_version: 3.1.6
191
+ signing_key:
175
192
  specification_version: 4
176
193
  summary: Minify HTML/CSS/JavaScript files.
177
194
  test_files: []