htmlnano 2.0.0 → 2.0.2

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/lib/htmlnano.js CHANGED
@@ -65,6 +65,9 @@ const optionalDependencies = {
65
65
  function htmlnano(optionsRun, presetRun) {
66
66
  let [options, preset] = loadConfig(optionsRun, presetRun);
67
67
  return function minifier(tree) {
68
+ const nodeHandlers = [];
69
+ const attrsHandlers = [];
70
+ const contentsHandlers = [];
68
71
  options = { ...preset,
69
72
  ...options
70
73
  };
@@ -92,12 +95,66 @@ function htmlnano(optionsRun, presetRun) {
92
95
  }
93
96
  });
94
97
 
95
- let module = require('./modules/' + moduleName);
98
+ const module = require('./modules/' + moduleName);
96
99
 
97
- promise = promise.then(tree => module.default(tree, options, moduleOptions));
100
+ if (typeof module.onAttrs === 'function') {
101
+ attrsHandlers.push(module.onAttrs(options, moduleOptions));
102
+ }
103
+
104
+ if (typeof module.onContent === 'function') {
105
+ contentsHandlers.push(module.onContent(options, moduleOptions));
106
+ }
107
+
108
+ if (typeof module.onNode === 'function') {
109
+ nodeHandlers.push(module.onNode(options, moduleOptions));
110
+ }
111
+
112
+ if (typeof module.default === 'function') {
113
+ promise = promise.then(tree => module.default(tree, options, moduleOptions));
114
+ }
115
+ }
116
+
117
+ if (attrsHandlers.length + contentsHandlers.length + nodeHandlers.length === 0) {
118
+ return promise;
98
119
  }
99
120
 
100
- return promise;
121
+ return promise.then(tree => {
122
+ tree.walk(node => {
123
+ if (node) {
124
+ if (node.attrs && typeof node.attrs === 'object') {
125
+ // Convert all attrs' key to lower case
126
+ let newAttrsObj = {};
127
+ Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
128
+ newAttrsObj[attrName.toLowerCase()] = attrValue;
129
+ });
130
+
131
+ for (const handler of attrsHandlers) {
132
+ newAttrsObj = handler(newAttrsObj, node);
133
+ }
134
+
135
+ node.attrs = newAttrsObj;
136
+ }
137
+
138
+ if (node.content) {
139
+ node.content = typeof node.content === 'string' ? [node.content] : node.content;
140
+
141
+ if (Array.isArray(node.content) && node.content.length > 0) {
142
+ for (const handler of contentsHandlers) {
143
+ const result = handler(node.content, node);
144
+ node.content = typeof result === 'string' ? [result] : result;
145
+ }
146
+ }
147
+ }
148
+
149
+ for (const handler of nodeHandlers) {
150
+ node = handler(node);
151
+ }
152
+ }
153
+
154
+ return node;
155
+ });
156
+ return tree;
157
+ });
101
158
  };
102
159
  }
103
160
 
@@ -4,14 +4,21 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.attributesWithLists = void 0;
7
- exports.default = collapseAttributeWhitespace;
7
+ exports.onAttrs = onAttrs;
8
8
 
9
9
  var _helpers = require("../helpers");
10
10
 
11
11
  const attributesWithLists = new Set(['class', 'dropzone', 'rel', // a, area, link
12
12
  'ping', // a, area
13
13
  'sandbox', // iframe
14
- 'sizes', // link
14
+
15
+ /**
16
+ * https://github.com/posthtml/htmlnano/issues/180
17
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes
18
+ *
19
+ * "sizes" of <img> should not be modified, while "sizes" of <link> will only have one entry in most cases.
20
+ */
21
+ // 'sizes', // link
15
22
  'headers' // td, th
16
23
  ]);
17
24
  /** @type Record<string, string[] | null> */
@@ -58,29 +65,22 @@ const attributesWithSingleValue = {
58
65
  };
59
66
  /** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
60
67
 
61
- function collapseAttributeWhitespace(tree) {
62
- tree.walk(node => {
63
- if (!node.attrs) {
64
- return node;
65
- }
66
-
67
- Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
68
- const attrNameLower = attrName.toLowerCase();
69
-
70
- if (attributesWithLists.has(attrNameLower)) {
68
+ function onAttrs() {
69
+ return (attrs, node) => {
70
+ const newAttrs = attrs;
71
+ Object.entries(attrs).forEach(([attrName, attrValue]) => {
72
+ if (attributesWithLists.has(attrName)) {
71
73
  const newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
72
- node.attrs[attrName] = newAttrValue;
73
- return node;
74
+ newAttrs[attrName] = newAttrValue;
75
+ return;
74
76
  }
75
77
 
76
- if ((0, _helpers.isEventHandler)(attrNameLower) || Object.hasOwnProperty.call(attributesWithSingleValue, attrNameLower) && (attributesWithSingleValue[attrNameLower] === null || attributesWithSingleValue[attrNameLower].includes(node.tag))) {
77
- node.attrs[attrName] = minifySingleAttributeValue(attrValue);
78
- return node;
78
+ if ((0, _helpers.isEventHandler)(attrName) || Object.hasOwnProperty.call(attributesWithSingleValue, attrName) && (attributesWithSingleValue[attrName] === null || attributesWithSingleValue[attrName].includes(node.tag))) {
79
+ newAttrs[attrName] = minifySingleAttributeValue(attrValue);
79
80
  }
80
81
  });
81
- return node;
82
- });
83
- return tree;
82
+ return newAttrs;
83
+ };
84
84
  }
85
85
 
86
86
  function minifySingleAttributeValue(value) {
@@ -3,42 +3,36 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = collapseBooleanAttributes;
6
+ exports.onAttrs = onAttrs;
7
7
  // Source: https://github.com/kangax/html-minifier/issues/63
8
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
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
- function collapseBooleanAttributes(tree, options, moduleOptions) {
12
- tree.walk(node => {
13
- if (!node.attrs) {
14
- return node;
15
- }
16
-
17
- if (!node.tag) {
18
- return node;
19
- }
11
+ function onAttrs(options, moduleOptions) {
12
+ return (attrs, node) => {
13
+ if (!node.tag) return attrs;
14
+ const newAttrs = attrs;
20
15
 
21
- for (const attrName of Object.keys(node.attrs)) {
16
+ for (const attrName of Object.keys(attrs)) {
22
17
  if (attrName === 'visible' && node.tag.startsWith('a-')) {
23
18
  continue;
24
19
  }
25
20
 
26
21
  if (htmlBooleanAttributes.has(attrName)) {
27
- node.attrs[attrName] = true;
22
+ newAttrs[attrName] = true;
28
23
  }
29
24
 
30
- if (moduleOptions.amphtml && amphtmlBooleanAttributes.has(attrName) && node.attrs[attrName] === '') {
31
- node.attrs[attrName] = true;
25
+ if (moduleOptions.amphtml && amphtmlBooleanAttributes.has(attrName) && attrs[attrName] === '') {
26
+ newAttrs[attrName] = true;
32
27
  } // collapse crossorigin attributes
33
28
  // Specification: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
34
29
 
35
30
 
36
- if (attrName.toLowerCase() === 'crossorigin' && (node.attrs[attrName] === 'anonymous' || node.attrs[attrName] === '')) {
37
- node.attrs[attrName] = true;
31
+ if (attrName.toLowerCase() === 'crossorigin' && (attrs[attrName] === 'anonymous' || attrs[attrName] === '')) {
32
+ newAttrs[attrName] = true;
38
33
  }
39
34
  }
40
35
 
41
- return node;
42
- });
43
- return tree;
36
+ return newAttrs;
37
+ };
44
38
  }
@@ -3,25 +3,20 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = collapseAttributeWhitespace;
6
+ exports.onAttrs = onAttrs;
7
7
 
8
8
  var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
9
9
 
10
10
  /** Deduplicate values inside list-like attributes (e.g. class, rel) */
11
- function collapseAttributeWhitespace(tree) {
12
- tree.walk(node => {
13
- if (!node.attrs) {
14
- return node;
15
- }
16
-
17
- Object.keys(node.attrs).forEach(attrName => {
18
- const attrNameLower = attrName.toLowerCase();
19
-
20
- if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
11
+ function onAttrs() {
12
+ return attrs => {
13
+ const newAttrs = attrs;
14
+ Object.keys(attrs).forEach(attrName => {
15
+ if (!_collapseAttributeWhitespace.attributesWithLists.has(attrName)) {
21
16
  return;
22
17
  }
23
18
 
24
- const attrValues = node.attrs[attrName].split(/\s/);
19
+ const attrValues = attrs[attrName].split(/\s/);
25
20
  const uniqeAttrValues = new Set();
26
21
  const deduplicatedAttrValues = [];
27
22
  attrValues.forEach(attrValue => {
@@ -38,9 +33,8 @@ function collapseAttributeWhitespace(tree) {
38
33
  deduplicatedAttrValues.push(attrValue);
39
34
  uniqeAttrValues.add(attrValue);
40
35
  });
41
- node.attrs[attrName] = deduplicatedAttrValues.join(' ');
36
+ newAttrs[attrName] = deduplicatedAttrValues.join(' ');
42
37
  });
43
- return node;
44
- });
45
- return tree;
38
+ return newAttrs;
39
+ };
46
40
  }
@@ -3,31 +3,19 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = minifyJson;
6
+ exports.onContent = onContent;
7
+ const rNodeAttrsTypeJson = /(\/|\+)json/;
7
8
 
8
- /* Minify JSON inside <script> tags */
9
- function minifyJson(tree) {
10
- // Match all <script> tags which have JSON mime type
11
- tree.match({
12
- tag: 'script',
13
- attrs: {
14
- type: /(\/|\+)json/
9
+ function onContent() {
10
+ return (content, node) => {
11
+ if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
12
+ try {
13
+ // cast minified JSON to an array
14
+ return [JSON.stringify(JSON.parse((content || []).join('')))];
15
+ } catch (error) {// Invalid JSON
16
+ }
15
17
  }
16
- }, node => {
17
- let content = (node.content || []).join('');
18
18
 
19
- if (!content) {
20
- return node;
21
- }
22
-
23
- try {
24
- content = JSON.stringify(JSON.parse(content));
25
- } catch (error) {
26
- return node;
27
- }
28
-
29
- node.content = [content];
30
- return node;
31
- });
32
- return tree;
19
+ return content;
20
+ };
33
21
  }
@@ -21,8 +21,9 @@ function minifySvg(tree, options, svgoOptions = {}) {
21
21
  });
22
22
  const result = svgo.optimize(svgStr, svgoOptions);
23
23
  node.tag = false;
24
- node.attrs = {};
25
- node.content = result.data;
24
+ node.attrs = {}; // result.data is a string, we need to cast it to an array
25
+
26
+ node.content = [result.data];
26
27
  return node;
27
28
  });
28
29
  return tree;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = normalizeAttributeValues;
6
+ exports.onAttrs = onAttrs;
7
7
  const caseInsensitiveAttributes = {
8
8
  autocomplete: ['form'],
9
9
  charset: ['meta', 'script'],
@@ -29,20 +29,14 @@ const caseInsensitiveAttributes = {
29
29
  wrap: ['textarea']
30
30
  };
31
31
 
32
- function normalizeAttributeValues(tree) {
33
- tree.walk(node => {
34
- if (!node.attrs) {
35
- return node;
36
- }
37
-
38
- Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
39
- const attrNameLower = attrName.toLowerCase();
40
-
41
- if (Object.hasOwnProperty.call(caseInsensitiveAttributes, attrNameLower) && (caseInsensitiveAttributes[attrNameLower] === null || caseInsensitiveAttributes[attrNameLower].includes(node.tag))) {
42
- node.attrs[attrName] = attrValue.toLowerCase ? attrValue.toLowerCase() : attrValue;
32
+ function onAttrs() {
33
+ return (attrs, node) => {
34
+ const newAttrs = attrs;
35
+ Object.entries(attrs).forEach(([attrName, attrValue]) => {
36
+ if (Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName) && (caseInsensitiveAttributes[attrName] === null || caseInsensitiveAttributes[attrName].includes(node.tag))) {
37
+ newAttrs[attrName] = attrValue.toLowerCase ? attrValue.toLowerCase() : attrValue;
43
38
  }
44
39
  });
45
- return node;
46
- });
47
- return tree;
40
+ return newAttrs;
41
+ };
48
42
  }
@@ -3,28 +3,36 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeComments;
6
+ exports.onContent = onContent;
7
+ exports.onNode = onNode;
7
8
 
8
9
  var _helpers = require("../helpers");
9
10
 
10
11
  const MATCH_EXCERPT_REGEXP = /<!-- ?more ?-->/i;
11
12
  /** Removes HTML comments */
12
13
 
13
- function removeComments(tree, options, removeType) {
14
+ function onNode(options, removeType) {
14
15
  if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
15
16
  removeType = 'safe';
16
17
  }
17
18
 
18
- tree.walk(node => {
19
- if (node.contents && node.contents.length) {
20
- node.contents = node.contents.filter(content => !isCommentToRemove(content, removeType));
21
- } else if (isCommentToRemove(node, removeType)) {
22
- node = '';
19
+ return node => {
20
+ if (isCommentToRemove(node, removeType)) {
21
+ return '';
23
22
  }
24
23
 
25
24
  return node;
26
- });
27
- return tree;
25
+ };
26
+ }
27
+
28
+ function onContent(options, removeType) {
29
+ if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
30
+ removeType = 'safe';
31
+ }
32
+
33
+ return contents => {
34
+ return contents.filter(content => !isCommentToRemove(content, removeType));
35
+ };
28
36
  }
29
37
 
30
38
  function isCommentToRemove(text, removeType) {
@@ -3,7 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeEmptyAttributes;
6
+ exports.onAttrs = onAttrs;
7
+
8
+ var _helpers = require("../helpers");
9
+
7
10
  const safeToRemoveAttrs = {
8
11
  id: null,
9
12
  class: null,
@@ -52,25 +55,18 @@ const safeToRemoveAttrs = {
52
55
  value: ['button', 'input', 'li'],
53
56
  width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
54
57
  };
55
- /** Removes empty attributes */
56
-
57
- function removeEmptyAttributes(tree) {
58
- tree.walk(node => {
59
- if (!node.attrs) {
60
- return node;
61
- }
62
-
63
- Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
64
- const attrNameLower = attrName.toLowerCase();
65
58
 
66
- if (attrNameLower.slice(0, 2).toLowerCase() === 'on' && attrName.length >= 5 // Event Handler
67
- || Object.hasOwnProperty.call(safeToRemoveAttrs, attrNameLower) && (safeToRemoveAttrs[attrNameLower] === null || safeToRemoveAttrs[attrNameLower].includes(node.tag))) {
59
+ function onAttrs() {
60
+ return (attrs, node) => {
61
+ const newAttrs = { ...attrs
62
+ };
63
+ Object.entries(attrs).forEach(([attrName, attrValue]) => {
64
+ if ((0, _helpers.isEventHandler)(attrName) || Object.hasOwnProperty.call(safeToRemoveAttrs, attrName) && (safeToRemoveAttrs[attrName] === null || safeToRemoveAttrs[attrName].includes(node.tag))) {
68
65
  if (attrValue === '' || (attrValue || '').match(/^\s+$/)) {
69
- delete node.attrs[attrName];
66
+ delete newAttrs[attrName];
70
67
  }
71
68
  }
72
69
  });
73
- return node;
74
- });
75
- return tree;
70
+ return newAttrs;
71
+ };
76
72
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = removeRedundantAttributes;
6
+ exports.onAttrs = onAttrs;
7
7
  exports.redundantScriptTypes = void 0;
8
8
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
9
9
  const redundantScriptTypes = new Set(['application/javascript', 'application/ecmascript', 'application/x-ecmascript', 'application/x-javascript', 'text/javascript', 'text/ecmascript', 'text/javascript1.0', 'text/javascript1.1', 'text/javascript1.2', 'text/javascript1.3', 'text/javascript1.4', 'text/javascript1.5', 'text/jscript', 'text/livescript', 'text/x-ecmascript', 'text/x-javascript']);
@@ -20,8 +20,8 @@ const redundantAttributes = {
20
20
  },
21
21
  'script': {
22
22
  'language': 'javascript',
23
- 'type': node => {
24
- for (const [attrName, attrValue] of Object.entries(node.attrs)) {
23
+ 'type': attrs => {
24
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
25
25
  if (attrName.toLowerCase() !== 'type') {
26
26
  continue;
27
27
  }
@@ -32,10 +32,10 @@ const redundantAttributes = {
32
32
  return false;
33
33
  },
34
34
  // Remove attribute if the function returns false
35
- 'charset': node => {
35
+ 'charset': attrs => {
36
36
  // The charset attribute only really makes sense on “external” SCRIPT elements:
37
37
  // http://perfectionkills.com/optimizing-html/#8_script_charset
38
- return node.attrs && !node.attrs.src;
38
+ return !attrs.src;
39
39
  }
40
40
  },
41
41
  'style': {
@@ -44,13 +44,13 @@ const redundantAttributes = {
44
44
  },
45
45
  'link': {
46
46
  'media': 'all',
47
- 'type': node => {
47
+ 'type': attrs => {
48
48
  // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
49
49
  let isRelStyleSheet = false;
50
50
  let isTypeTextCSS = false;
51
51
 
52
- if (node.attrs) {
53
- for (const [attrName, attrValue] of Object.entries(node.attrs)) {
52
+ if (attrs) {
53
+ for (const [attrName, attrValue] of Object.entries(attrs)) {
54
54
  if (attrName.toLowerCase() === 'rel' && attrValue === 'stylesheet') {
55
55
  isRelStyleSheet = true;
56
56
  }
@@ -115,13 +115,10 @@ const tagsHaveRedundantAttributes = new Set(Object.keys(redundantAttributes));
115
115
  const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(canBeReplacedWithEmptyStringAttributes));
116
116
  /** Removes redundant attributes */
117
117
 
118
- function removeRedundantAttributes(tree) {
119
- tree.walk(node => {
120
- if (!node.tag) {
121
- return node;
122
- }
123
-
124
- node.attrs = node.attrs || {};
118
+ function onAttrs() {
119
+ return (attrs, node) => {
120
+ if (!node.tag) return attrs;
121
+ const newAttrs = attrs;
125
122
 
126
123
  if (tagsHaveRedundantAttributes.has(node.tag)) {
127
124
  const tagRedundantAttributes = redundantAttributes[node.tag];
@@ -131,13 +128,13 @@ function removeRedundantAttributes(tree) {
131
128
  let isRemove = false;
132
129
 
133
130
  if (typeof tagRedundantAttributeValue === 'function') {
134
- isRemove = tagRedundantAttributeValue(node);
135
- } else if (node.attrs[redundantAttributeName] === tagRedundantAttributeValue) {
131
+ isRemove = tagRedundantAttributeValue(attrs);
132
+ } else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
136
133
  isRemove = true;
137
134
  }
138
135
 
139
136
  if (isRemove) {
140
- delete node.attrs[redundantAttributeName];
137
+ delete newAttrs[redundantAttributeName];
141
138
  }
142
139
  }
143
140
  }
@@ -149,17 +146,16 @@ function removeRedundantAttributes(tree) {
149
146
  let tagMissingValueDefaultAttribute = tagMissingValueDefaultAttributes[canBeReplacedWithEmptyStringAttributeName];
150
147
  let isReplace = false;
151
148
 
152
- if (node.attrs[canBeReplacedWithEmptyStringAttributeName] === tagMissingValueDefaultAttribute) {
149
+ if (attrs[canBeReplacedWithEmptyStringAttributeName] === tagMissingValueDefaultAttribute) {
153
150
  isReplace = true;
154
151
  }
155
152
 
156
153
  if (isReplace) {
157
- node.attrs[canBeReplacedWithEmptyStringAttributeName] = '';
154
+ newAttrs[canBeReplacedWithEmptyStringAttributeName] = '';
158
155
  }
159
156
  }
160
157
  }
161
158
 
162
- return node;
163
- });
164
- return tree;
159
+ return newAttrs;
160
+ };
165
161
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htmlnano",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Modular HTML minifier, built on top of the PostHTML",
5
5
  "main": "index.js",
6
6
  "author": "Kirill Maltsev <maltsevkirill@gmail.com>",
@@ -46,11 +46,11 @@
46
46
  "devDependencies": {
47
47
  "@babel/cli": "^7.15.7",
48
48
  "@babel/core": "^7.15.5",
49
+ "@babel/eslint-parser": "^7.17.0",
49
50
  "@babel/preset-env": "^7.15.6",
50
51
  "@babel/register": "^7.15.3",
51
- "babel-eslint": "^10.1.0",
52
52
  "cssnano": "^5.0.11",
53
- "eslint": "^7.32.0",
53
+ "eslint": "^8.12.0",
54
54
  "expect": "^27.2.0",
55
55
  "mocha": "^9.1.0",
56
56
  "postcss": "^8.3.11",