htmlnano 0.2.7 → 0.2.8

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
 
6
+ ## [0.2.8] - 2020-11-15
7
+ ### Added
8
+ - [`removeOptionalTags`](https://github.com/posthtml/htmlnano#removeoptionaltags) [#110].
9
+ - [`sortAttributes`](https://github.com/posthtml/htmlnano#removeoptionaltags) [#113].
10
+ - `source[src]` and `srcset` support to `minifyUrls` [#117].
11
+ - [`minifyConditionalComments`](https://github.com/posthtml/htmlnano#minifyconditionalcomments) [#119].
12
+
13
+ ### Changed
14
+ - Sort by frequency `sortAttributesWithLists` [#111].
15
+ - Strip more spaces in `collapseWhitespace` [#112].
16
+ - Remove `loading="eager"` from `<img>` and `<iframe>` [#114].
17
+ - Remove redundant `type` from `<script>` [#114].
18
+ - Strip whitespaces between textnode and element [#116].
19
+
20
+
21
+
6
22
  ## [0.2.7] - 2020-10-17
7
23
  ### Added
8
24
  - More aggressive whitespace removal option [#90].
@@ -152,6 +168,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
152
168
  - Remove attributes that contains only white spaces.
153
169
 
154
170
 
171
+ [0.2.8]: https://github.com/posthtml/htmlnano/compare/0.2.7...0.2.8
155
172
  [0.2.7]: https://github.com/posthtml/htmlnano/compare/0.2.6...0.2.7
156
173
  [0.2.6]: https://github.com/posthtml/htmlnano/compare/0.2.5...0.2.6
157
174
  [0.2.5]: https://github.com/posthtml/htmlnano/compare/0.2.4...0.2.5
@@ -172,6 +189,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
172
189
  [0.1.1]: https://github.com/posthtml/htmlnano/compare/0.1.0...0.1.1
173
190
 
174
191
 
192
+ [#119]: https://github.com/posthtml/htmlnano/issues/119
193
+ [#117]: https://github.com/posthtml/htmlnano/issues/117
194
+ [#116]: https://github.com/posthtml/htmlnano/issues/116
195
+ [#114]: https://github.com/posthtml/htmlnano/issues/114
196
+ [#113]: https://github.com/posthtml/htmlnano/issues/113
197
+ [#112]: https://github.com/posthtml/htmlnano/issues/112
198
+ [#111]: https://github.com/posthtml/htmlnano/issues/111
199
+ [#110]: https://github.com/posthtml/htmlnano/issues/110
175
200
  [#107]: https://github.com/posthtml/htmlnano/issues/107
176
201
  [#108]: https://github.com/posthtml/htmlnano/issues/108
177
202
  [#102]: https://github.com/posthtml/htmlnano/issues/102
package/README.md CHANGED
@@ -529,6 +529,49 @@ Minified:
529
529
  <svg baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>
530
530
  ```
531
531
 
532
+ ### minifyConditionalComments
533
+
534
+ Minify content inside conditional comments.
535
+
536
+ ##### Example
537
+
538
+ Source:
539
+
540
+ ```html
541
+ <!--[if lte IE 7]>
542
+ <style type="text/css">
543
+ .title {
544
+ color: red;
545
+ }
546
+ </style>
547
+ <![endif]-->
548
+ ```
549
+
550
+ Minified:
551
+
552
+ ```html
553
+ <!--[if lte IE 7]><style>.title {color:red}</style><![endif]-->
554
+ ```
555
+
556
+ ##### Notice
557
+
558
+ Due to [the limitation of PostHTML](https://github.com/posthtml/posthtml-parser/issues/9) (which is actually a issue from upstream [htmlparser2](https://github.com/fb55/htmlparser2/pull/146)), following html snippet is not supported:
559
+
560
+ ```html
561
+ <!--[if lt IE 7]><html class="no-js ie6"><![endif]-->
562
+ <!--[if IE 7]><html class="no-js ie7"><![endif]-->
563
+ <!--[if IE 8]><html class="no-js ie8"><![endif]-->
564
+ <!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]-->
565
+ ```
566
+
567
+ Which will result in:
568
+
569
+ ```html
570
+ <!--[if lt IE 7]><html class="no-js ie6"></html><![endif]-->
571
+ <!--[if IE 7]><html class="no-js ie7"></html><![endif]-->
572
+ <!--[if IE 8]><html class="no-js ie8"></html><![endif]-->
573
+ <!--[if gt IE 8]><!--><html class="no-js"></html><!--<![endif]-->
574
+ ```
532
575
 
533
576
  ### removeRedundantAttributes
534
577
  Removes redundant attributes from tags if they contain default values:
@@ -685,7 +728,15 @@ Sort values in list-like attributes (`class`, `rel`, `ping`).
685
728
 
686
729
  The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.
687
730
 
731
+ ##### Options
732
+
733
+ - `alphabetical`: Default option. Sort attribute values in alphabetical order.
734
+ - `frequency`: Sort attribute values by frequency.
735
+
688
736
  ##### Example
737
+
738
+ **alphabetical**
739
+
689
740
  Source:
690
741
  ```html
691
742
  <div class="foo baz bar">click</div>
@@ -696,6 +747,58 @@ Processed:
696
747
  <div class="bar baz foo">click</div>
697
748
  ```
698
749
 
750
+ **frequency**
751
+
752
+ Source:
753
+ ```html
754
+ <div class="foo baz bar"></div><div class="bar foo"></div>
755
+ ```
756
+
757
+ Processed:
758
+ ```html
759
+ <div class="foo bar baz"></div><div class="foo bar"></div>
760
+ ```
761
+
762
+ ### sortAttributes
763
+ Sort attributes inside elements.
764
+
765
+ The module won't impact the plain-text size of the output. However it will improve the compression ratio of gzip/brotli used in HTTP compression.
766
+
767
+ ##### Options
768
+
769
+ - `alphabetical`: Default option. Sort attributes in alphabetical order.
770
+ - `frequency`: Sort attributes by frequency.
771
+
772
+ ##### Example
773
+
774
+ **alphabetical**
775
+
776
+ Source:
777
+ ```html
778
+ <input type="text" class="form-control" name="testInput" autofocus="" autocomplete="off" id="testId">
779
+ ```
780
+
781
+ Processed:
782
+ ```html
783
+ <input autocomplete="off" autofocus="" class="form-control" id="testId" name="testInput" type="text">
784
+ ```
785
+
786
+ **frequency**
787
+
788
+ Source:
789
+ ```html
790
+ <input type="text" class="form-control" name="testInput" id="testId">
791
+ <a id="testId" href="#" class="testClass"></a>
792
+ <img width="20" src="../images/image.png" height="40" alt="image" class="cls" id="id2">
793
+ ```
794
+
795
+ Processed:
796
+ ```html
797
+ <input class="form-control" id="testId" type="text" name="testInput">
798
+ <a class="testClass" id="testId" href="#"></a>
799
+ <img class="cls" id="id2" width="20" src="../images/image.png" height="40" alt="image">
800
+ ```
801
+
699
802
  ### minifyUrls
700
803
  Convert absolute URL to relative URL using [relateurl](https://www.npmjs.com/package/relateurl).
701
804
 
@@ -773,6 +876,31 @@ Minified:
773
876
  <a href="../bar">bar</a>
774
877
  ```
775
878
 
879
+ ## removeOptionalTags
880
+ Remove certain tags that can be omitted, see [HTML Standard - 13.1.2.4 Optional tags](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags).
881
+
882
+ ##### Example
883
+
884
+ Source:
885
+
886
+ ```html
887
+ <html><head><title>Title</title></head><body><p>Hi</p></body></html>
888
+ ```
889
+
890
+ Minified:
891
+
892
+ ```html
893
+ <title>Title</title><p>Hi</p>
894
+ ```
895
+ ##### Notice
896
+ Due to [the limitation of PostHTML](https://github.com/posthtml/htmlnano/issues/99), htmlnano can't remove only the start tag or the end tag of an element. Currently, htmlnano only supports removing the following optional tags, as htmlnano can remove their start tag and end tag at the same time:
897
+
898
+ - `html`
899
+ - `head`
900
+ - `body`
901
+ - `colgroup`
902
+ - `tbody`
903
+
776
904
  ## Contribute
777
905
  Since the minifier is modular, it's very easy to add new modules:
778
906
 
package/lib/helpers.js CHANGED
@@ -8,35 +8,17 @@ exports.isComment = isComment;
8
8
  exports.isConditionalComment = isConditionalComment;
9
9
  exports.isStyleNode = isStyleNode;
10
10
  exports.extractCssFromStyleNode = extractCssFromStyleNode;
11
-
12
- function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
13
-
14
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
15
-
16
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
17
-
18
- var ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
11
+ const ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
19
12
 
20
13
  function isAmpBoilerplate(node) {
21
14
  if (!node.attrs) {
22
15
  return false;
23
16
  }
24
17
 
25
- var _iterator = _createForOfIteratorHelper(ampBoilerplateAttributes),
26
- _step;
27
-
28
- try {
29
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
30
- var attr = _step.value;
31
-
32
- if (attr in node.attrs) {
33
- return true;
34
- }
18
+ for (const attr of ampBoilerplateAttributes) {
19
+ if (attr in node.attrs) {
20
+ return true;
35
21
  }
36
- } catch (err) {
37
- _iterator.e(err);
38
- } finally {
39
- _iterator.f();
40
22
  }
41
23
 
42
24
  return false;
package/lib/htmlnano.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = void 0;
6
+ exports.default = void 0;
7
7
 
8
8
  var _posthtml = _interopRequireDefault(require("posthtml"));
9
9
 
@@ -13,58 +13,28 @@ var _ampSafe = _interopRequireDefault(require("./presets/ampSafe"));
13
13
 
14
14
  var _max = _interopRequireDefault(require("./presets/max"));
15
15
 
16
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
17
 
18
- function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
19
-
20
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
21
-
22
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
23
-
24
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
25
-
26
- function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
27
-
28
- function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
29
-
30
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
31
-
32
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
33
-
34
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
35
-
36
- function htmlnano() {
37
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
38
- var preset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _safe["default"];
18
+ function htmlnano(options = {}, preset = _safe.default) {
39
19
  return function minifier(tree) {
40
- options = _objectSpread(_objectSpread({}, preset), options);
41
- var promise = Promise.resolve(tree);
42
-
43
- var _loop = function _loop() {
44
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
45
- moduleName = _Object$entries$_i[0],
46
- moduleOptions = _Object$entries$_i[1];
20
+ options = { ...preset,
21
+ ...options
22
+ };
23
+ let promise = Promise.resolve(tree);
47
24
 
25
+ for (const [moduleName, moduleOptions] of Object.entries(options)) {
48
26
  if (!moduleOptions) {
49
27
  // The module is disabled
50
- return "continue";
28
+ continue;
51
29
  }
52
30
 
53
- if (_safe["default"][moduleName] === undefined) {
31
+ if (_safe.default[moduleName] === undefined) {
54
32
  throw new Error('Module "' + moduleName + '" is not defined');
55
33
  }
56
34
 
57
- var module = require('./modules/' + moduleName);
58
-
59
- promise = promise.then(function (tree) {
60
- return module["default"](tree, options, moduleOptions);
61
- });
62
- };
63
-
64
- for (var _i = 0, _Object$entries = Object.entries(options); _i < _Object$entries.length; _i++) {
65
- var _ret = _loop();
35
+ let module = require('./modules/' + moduleName);
66
36
 
67
- if (_ret === "continue") continue;
37
+ promise = promise.then(tree => module.default(tree, options, moduleOptions));
68
38
  }
69
39
 
70
40
  return promise;
@@ -72,13 +42,13 @@ function htmlnano() {
72
42
  }
73
43
 
74
44
  htmlnano.process = function (html, options, preset, postHtmlOptions) {
75
- return (0, _posthtml["default"])([htmlnano(options, preset)]).process(html, postHtmlOptions);
45
+ return (0, _posthtml.default)([htmlnano(options, preset)]).process(html, postHtmlOptions);
76
46
  };
77
47
 
78
48
  htmlnano.presets = {
79
- safe: _safe["default"],
80
- ampSafe: _ampSafe["default"],
81
- max: _max["default"]
49
+ safe: _safe.default,
50
+ ampSafe: _ampSafe.default,
51
+ max: _max.default
82
52
  };
83
53
  var _default = htmlnano;
84
- exports["default"] = _default;
54
+ exports.default = _default;
@@ -3,44 +3,27 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = collapseAttributeWhitespace;
6
+ exports.default = collapseAttributeWhitespace;
7
7
  exports.attributesWithLists = void 0;
8
-
9
- function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
10
-
11
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
12
-
13
- function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
14
-
15
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
16
-
17
- function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
18
-
19
- function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
20
-
21
- var attributesWithLists = new Set(['class', 'rel', 'ping']);
8
+ const attributesWithLists = new Set(['class', 'rel', 'ping']);
22
9
  /** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
23
10
 
24
11
  exports.attributesWithLists = attributesWithLists;
25
12
 
26
13
  function collapseAttributeWhitespace(tree) {
27
- tree.walk(function (node) {
14
+ tree.walk(node => {
28
15
  if (!node.attrs) {
29
16
  return node;
30
17
  }
31
18
 
32
- Object.entries(node.attrs).forEach(function (_ref) {
33
- var _ref2 = _slicedToArray(_ref, 2),
34
- attrName = _ref2[0],
35
- attrValue = _ref2[1];
36
-
37
- var attrNameLower = attrName.toLowerCase();
19
+ Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
20
+ const attrNameLower = attrName.toLowerCase();
38
21
 
39
22
  if (!attributesWithLists.has(attrNameLower)) {
40
23
  return;
41
24
  }
42
25
 
43
- var newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
26
+ const newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
44
27
  node.attrs[attrName] = newAttrValue;
45
28
  });
46
29
  return node;
@@ -3,18 +3,16 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = collapseBooleanAttributes;
6
+ exports.default = collapseBooleanAttributes;
7
7
  // Source: https://github.com/kangax/html-minifier/issues/63
8
- var htmlBooleanAttributes = new Set(['allowfullscreen', 'allowpaymentrequest', 'allowtransparency', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', 'defaultchecked', 'defaultmuted', 'defaultselected', 'defer', 'disabled', 'enabled', 'formnovalidate', 'hidden', 'indeterminate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch', 'visible']);
9
- var amphtmlBooleanAttributes = new Set(['⚡', 'amp', '⚡4ads', 'amp4ads', '⚡4email', 'amp4email', 'amp-custom', 'amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate', 'allow-blocked-ranges', 'amp-access-hide', 'amp-access-template', 'amp-keyframes', 'animate', 'arrows', 'data-block-on-consent', 'data-enable-refresh', 'data-multi-size', 'date-template', 'disable-double-tap', 'disable-session-states', 'disableremoteplayback', 'dots', 'expand-single-section', 'expanded', 'fallback', 'first', 'fullscreen', 'inline', 'lightbox', 'noaudio', 'noautoplay', 'noloading', 'once', 'open-after-clear', 'open-after-select', 'open-button', 'placeholder', 'preload', 'reset-on-refresh', 'reset-on-resize', 'resizable', 'rotate-to-fullscreen', 'second', 'standalone', 'stereo', 'submit-error', 'submit-success', 'submitting', 'subscriptions-actions', 'subscriptions-dialog']);
8
+ const htmlBooleanAttributes = new Set(['allowfullscreen', 'allowpaymentrequest', 'allowtransparency', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', 'defaultchecked', 'defaultmuted', 'defaultselected', 'defer', 'disabled', 'enabled', 'formnovalidate', 'hidden', 'indeterminate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch', 'visible']);
9
+ const amphtmlBooleanAttributes = new Set(['⚡', 'amp', '⚡4ads', 'amp4ads', '⚡4email', 'amp4email', 'amp-custom', 'amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate', 'allow-blocked-ranges', 'amp-access-hide', 'amp-access-template', 'amp-keyframes', 'animate', 'arrows', 'data-block-on-consent', 'data-enable-refresh', 'data-multi-size', 'date-template', 'disable-double-tap', 'disable-session-states', 'disableremoteplayback', 'dots', 'expand-single-section', 'expanded', 'fallback', 'first', 'fullscreen', 'inline', 'lightbox', 'noaudio', 'noautoplay', 'noloading', 'once', 'open-after-clear', 'open-after-select', 'open-button', 'placeholder', 'preload', 'reset-on-refresh', 'reset-on-resize', 'resizable', 'rotate-to-fullscreen', 'second', 'standalone', 'stereo', 'submit-error', 'submit-success', 'submitting', 'subscriptions-actions', 'subscriptions-dialog']);
10
10
 
11
11
  function collapseBooleanAttributes(tree, options, moduleOptions) {
12
12
  tree.match({
13
13
  attrs: true
14
- }, function (node) {
15
- for (var _i = 0, _Object$keys = Object.keys(node.attrs); _i < _Object$keys.length; _i++) {
16
- var attrName = _Object$keys[_i];
17
-
14
+ }, node => {
15
+ for (const attrName of Object.keys(node.attrs)) {
18
16
  if (!node.tag) {
19
17
  continue;
20
18
  }
@@ -3,33 +3,33 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = collapseWhitespace;
6
+ exports.default = collapseWhitespace;
7
7
 
8
8
  var _helpers = require("../helpers");
9
9
 
10
- var noWhitespaceCollapseElements = new Set(['script', 'style', 'pre', 'textarea']);
11
- var noTrimWhitespacesArroundElements = new Set([// non-empty tags that will maintain whitespace around them
10
+ const noWhitespaceCollapseElements = new Set(['script', 'style', 'pre', 'textarea']);
11
+ const noTrimWhitespacesArroundElements = new Set([// non-empty tags that will maintain whitespace around them
12
12
  'a', 'abbr', 'acronym', 'b', 'bdi', 'bdo', 'big', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'ins', 'kbd', 'label', 'mark', 'math', 'nobr', 'object', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var', // self-closing tags that will maintain whitespace around them
13
13
  'comment', 'img', 'input', 'wbr']);
14
- var noTrimWhitespacesInsideElements = new Set([// non-empty tags that will maintain whitespace within them
14
+ const noTrimWhitespacesInsideElements = new Set([// non-empty tags that will maintain whitespace within them
15
15
  'a', 'abbr', 'acronym', 'b', 'big', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'mark', 'nobr', 'rp', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var']);
16
- var whitespacePattern = /[\f\n\r\t\v ]{1,}/g;
17
- var onlyWhitespacePattern = /^[\f\n\r\t\v ]+$/;
18
- var NONE = '';
19
- var SINGLE_SPACE = ' ';
20
- var validOptions = ['all', 'aggressive', 'conservative'];
16
+ const whitespacePattern = /\s{1,}/g;
17
+ const onlyWhitespacePattern = /^\s+$/;
18
+ const NONE = '';
19
+ const SINGLE_SPACE = ' ';
20
+ const validOptions = ['all', 'aggressive', 'conservative'];
21
21
  /** Collapses redundant whitespaces */
22
22
 
23
23
  function collapseWhitespace(tree, options, collapseType, tag) {
24
24
  collapseType = validOptions.includes(collapseType) ? collapseType : 'conservative';
25
- tree.forEach(function (node, index) {
25
+ tree.forEach((node, index) => {
26
26
  if (typeof node === 'string' && !(0, _helpers.isComment)(node)) {
27
- var prevNode = tree[index - 1];
28
- var nextNode = tree[index + 1];
29
- var prevNodeTag = prevNode && prevNode.tag;
30
- var nextNodeTag = nextNode && nextNode.tag;
31
- var isTopLevel = !tag || tag === 'html' || tag === 'head';
32
- var shouldTrim = collapseType === 'all' || isTopLevel ||
27
+ const prevNode = tree[index - 1];
28
+ const nextNode = tree[index + 1];
29
+ const prevNodeTag = prevNode && prevNode.tag;
30
+ const nextNodeTag = nextNode && nextNode.tag;
31
+ const isTopLevel = !tag || tag === 'html' || tag === 'head';
32
+ const shouldTrim = collapseType === 'all' || isTopLevel ||
33
33
  /*
34
34
  * When collapseType is set to 'aggressive', and the tag is not inside 'noTrimWhitespacesInsideElements'.
35
35
  * the first & last space inside the tag will be trimmed
@@ -38,7 +38,7 @@ function collapseWhitespace(tree, options, collapseType, tag) {
38
38
  node = collapseRedundantWhitespaces(node, collapseType, shouldTrim, tag, prevNodeTag, nextNodeTag);
39
39
  }
40
40
 
41
- var isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
41
+ const isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
42
42
 
43
43
  if (node.content && node.content.length && isAllowCollapseWhitespace) {
44
44
  node.content = collapseWhitespace(node.content, options, collapseType, node.tag);
@@ -49,12 +49,7 @@ function collapseWhitespace(tree, options, collapseType, tag) {
49
49
  return tree;
50
50
  }
51
51
 
52
- function collapseRedundantWhitespaces(text, collapseType) {
53
- var shouldTrim = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
54
- var currentTag = arguments.length > 3 ? arguments[3] : undefined;
55
- var prevNodeTag = arguments.length > 4 ? arguments[4] : undefined;
56
- var nextNodeTag = arguments.length > 5 ? arguments[5] : undefined;
57
-
52
+ function collapseRedundantWhitespaces(text, collapseType, shouldTrim = false, currentTag, prevNodeTag, nextNodeTag) {
58
53
  if (!text || text.length === 0) {
59
54
  return NONE;
60
55
  }
@@ -66,7 +61,7 @@ function collapseRedundantWhitespaces(text, collapseType) {
66
61
  if (onlyWhitespacePattern.test(text)) {
67
62
  // "text" only contains whitespaces. Only trim when both prevNodeTag & nextNodeTag are not "noTrimWhitespacesArroundElement"
68
63
  // Otherwise the required ONE whitespace will be trimmed
69
- if (!noTrimWhitespacesArroundElements.has(prevNodeTag) && !noTrimWhitespacesArroundElements.has(nextNodeTag)) {
64
+ if (!noTrimWhitespacesArroundElements.has(prevNodeTag) || !noTrimWhitespacesArroundElements.has(nextNodeTag)) {
70
65
  text = text.trim();
71
66
  }
72
67
  } else {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = custom;
6
+ exports.default = custom;
7
7
 
8
8
  /** Meta-module that runs custom modules */
9
9
  function custom(tree, options, customModules) {
@@ -15,7 +15,7 @@ function custom(tree, options, customModules) {
15
15
  customModules = [customModules];
16
16
  }
17
17
 
18
- customModules.forEach(function (customModule) {
18
+ customModules.forEach(customModule => {
19
19
  tree = customModule(tree, options);
20
20
  });
21
21
  return tree;
@@ -3,28 +3,28 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = collapseAttributeWhitespace;
6
+ exports.default = collapseAttributeWhitespace;
7
7
 
8
8
  var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
9
9
 
10
10
  /** Deduplicate values inside list-like attributes (e.g. class, rel) */
11
11
  function collapseAttributeWhitespace(tree) {
12
- tree.walk(function (node) {
12
+ tree.walk(node => {
13
13
  if (!node.attrs) {
14
14
  return node;
15
15
  }
16
16
 
17
- Object.keys(node.attrs).forEach(function (attrName) {
18
- var attrNameLower = attrName.toLowerCase();
17
+ Object.keys(node.attrs).forEach(attrName => {
18
+ const attrNameLower = attrName.toLowerCase();
19
19
 
20
20
  if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
21
21
  return;
22
22
  }
23
23
 
24
- var attrValues = node.attrs[attrName].split(/\s/);
25
- var uniqeAttrValues = new Set();
26
- var deduplicatedAttrValues = [];
27
- attrValues.forEach(function (attrValue) {
24
+ const attrValues = node.attrs[attrName].split(/\s/);
25
+ const uniqeAttrValues = new Set();
26
+ let deduplicatedAttrValues = [];
27
+ attrValues.forEach(attrValue => {
28
28
  if (!attrValue) {
29
29
  // Keep whitespaces
30
30
  deduplicatedAttrValues.push('');
@@ -3,31 +3,31 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports["default"] = mergeScripts;
6
+ exports.default = mergeScripts;
7
7
 
8
8
  /* Merge multiple <script> into one */
9
9
  function mergeScripts(tree) {
10
- var scriptNodesIndex = {};
11
- var scriptSrcIndex = 1;
10
+ let scriptNodesIndex = {};
11
+ let scriptSrcIndex = 1;
12
12
  tree.match({
13
13
  tag: 'script'
14
- }, function (node) {
15
- var nodeAttrs = node.attrs || {};
14
+ }, node => {
15
+ const nodeAttrs = node.attrs || {};
16
16
 
17
17
  if (nodeAttrs.src) {
18
18
  scriptSrcIndex++;
19
19
  return node;
20
20
  }
21
21
 
22
- var scriptType = nodeAttrs.type || 'text/javascript';
22
+ const scriptType = nodeAttrs.type || 'text/javascript';
23
23
 
24
24
  if (scriptType !== 'text/javascript' && scriptType !== 'application/javascript') {
25
25
  return node;
26
26
  }
27
27
 
28
- var scriptKey = JSON.stringify({
28
+ const scriptKey = JSON.stringify({
29
29
  id: nodeAttrs.id,
30
- "class": nodeAttrs["class"],
30
+ class: nodeAttrs.class,
31
31
  type: scriptType,
32
32
  defer: nodeAttrs.defer !== undefined,
33
33
  async: nodeAttrs.async !== undefined,
@@ -42,11 +42,10 @@ function mergeScripts(tree) {
42
42
  return node;
43
43
  });
44
44
 
45
- var _loop = function _loop() {
46
- var scriptNodes = _Object$values[_i];
47
- var lastScriptNode = scriptNodes.pop();
48
- scriptNodes.reverse().forEach(function (scriptNode) {
49
- var scriptContent = (scriptNode.content || []).join(' ');
45
+ for (const scriptNodes of Object.values(scriptNodesIndex)) {
46
+ let lastScriptNode = scriptNodes.pop();
47
+ scriptNodes.reverse().forEach(scriptNode => {
48
+ let scriptContent = (scriptNode.content || []).join(' ');
50
49
  scriptContent = scriptContent.trim();
51
50
 
52
51
  if (scriptContent.slice(-1) !== ';') {
@@ -58,10 +57,6 @@ function mergeScripts(tree) {
58
57
  scriptNode.tag = false;
59
58
  scriptNode.content = [];
60
59
  });
61
- };
62
-
63
- for (var _i = 0, _Object$values = Object.values(scriptNodesIndex); _i < _Object$values.length; _i++) {
64
- _loop();
65
60
  }
66
61
 
67
62
  return tree;