@utrecht/web-component-library-stencil 1.0.0-alpha.124 → 1.0.0-alpha.128

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.
Files changed (207) hide show
  1. package/dist/cjs/{index-06c6c66f.js → index-8cef21e1.js} +67 -58
  2. package/dist/cjs/loader.cjs.js +3 -3
  3. package/dist/cjs/utrecht-article.cjs.entry.js +1 -1
  4. package/dist/cjs/utrecht-backdrop.cjs.entry.js +21 -0
  5. package/dist/cjs/utrecht-badge-counter.cjs.entry.js +1 -1
  6. package/dist/cjs/utrecht-badge-data.cjs.entry.js +1 -1
  7. package/dist/cjs/utrecht-badge-status.cjs.entry.js +1 -1
  8. package/dist/cjs/utrecht-breadcrumb.cjs.entry.js +1 -1
  9. package/dist/cjs/utrecht-button_2.cjs.entry.js +1 -1
  10. package/dist/cjs/utrecht-checkbox.cjs.entry.js +1 -1
  11. package/dist/cjs/utrecht-contact-card-template.cjs.entry.js +1 -1
  12. package/dist/cjs/utrecht-custom-checkbox.cjs.entry.js +1 -1
  13. package/dist/cjs/utrecht-digid-button.cjs.entry.js +1 -1
  14. package/dist/cjs/utrecht-document.cjs.entry.js +1 -1
  15. package/dist/cjs/utrecht-eherkenning-logo.cjs.entry.js +1 -1
  16. package/dist/cjs/utrecht-eidas-logo.cjs.entry.js +1 -1
  17. package/dist/cjs/utrecht-form-field-checkbox.cjs.entry.js +1 -1
  18. package/dist/cjs/utrecht-form-field-description.cjs.entry.js +1 -1
  19. package/dist/cjs/utrecht-form-field-textarea.cjs.entry.js +1 -1
  20. package/dist/cjs/utrecht-form-field-textbox.cjs.entry.js +1 -1
  21. package/dist/cjs/utrecht-form-toggle.cjs.entry.js +1 -1
  22. package/dist/cjs/utrecht-heading-1.cjs.entry.js +1 -1
  23. package/dist/cjs/utrecht-heading-2_3.cjs.entry.js +1 -1
  24. package/dist/cjs/utrecht-heading-4.cjs.entry.js +1 -1
  25. package/dist/cjs/utrecht-heading-5.cjs.entry.js +1 -1
  26. package/dist/cjs/utrecht-heading-6.cjs.entry.js +1 -1
  27. package/dist/cjs/utrecht-heading.cjs.entry.js +1 -1
  28. package/dist/cjs/utrecht-html-content.cjs.entry.js +1 -1
  29. package/dist/cjs/utrecht-icon-afval.cjs.entry.js +1 -1
  30. package/dist/cjs/utrecht-icon-arrow.cjs.entry.js +1 -1
  31. package/dist/cjs/utrecht-icon-checkmark.cjs.entry.js +1 -1
  32. package/dist/cjs/utrecht-icon-cross.cjs.entry.js +1 -1
  33. package/dist/cjs/utrecht-icon-error.cjs.entry.js +1 -1
  34. package/dist/cjs/utrecht-icon-facebook.cjs.entry.js +1 -1
  35. package/dist/cjs/utrecht-icon-filter.cjs.entry.js +1 -1
  36. package/dist/cjs/utrecht-icon-information.cjs.entry.js +1 -1
  37. package/dist/cjs/utrecht-icon-instagram.cjs.entry.js +1 -1
  38. package/dist/cjs/utrecht-icon-linkedin.cjs.entry.js +1 -1
  39. package/dist/cjs/utrecht-icon-list.cjs.entry.js +1 -1
  40. package/dist/cjs/utrecht-icon-loupe.cjs.entry.js +1 -1
  41. package/dist/cjs/utrecht-icon-melding.cjs.entry.js +1 -1
  42. package/dist/cjs/utrecht-icon-paspoort.cjs.entry.js +1 -1
  43. package/dist/cjs/utrecht-icon-rijbewijs.cjs.entry.js +1 -1
  44. package/dist/cjs/utrecht-icon-toeslag.cjs.entry.js +1 -1
  45. package/dist/cjs/utrecht-icon-trouwen.cjs.entry.js +1 -1
  46. package/dist/cjs/utrecht-icon-twitter.cjs.entry.js +1 -1
  47. package/dist/cjs/utrecht-icon-verhuizen.cjs.entry.js +1 -1
  48. package/dist/cjs/utrecht-icon-warning.cjs.entry.js +1 -1
  49. package/dist/cjs/utrecht-icon-whatsapp.cjs.entry.js +1 -1
  50. package/dist/cjs/utrecht-icon-zoomin.cjs.entry.js +1 -1
  51. package/dist/cjs/utrecht-icon-zoomout.cjs.entry.js +1 -1
  52. package/dist/cjs/utrecht-logo-button.cjs.entry.js +1 -1
  53. package/dist/cjs/utrecht-logo.cjs.entry.js +1 -1
  54. package/dist/cjs/utrecht-page-content.cjs.entry.js +1 -1
  55. package/dist/cjs/utrecht-page-footer.cjs.entry.js +1 -1
  56. package/dist/cjs/utrecht-page-header.cjs.entry.js +1 -1
  57. package/dist/cjs/utrecht-page.cjs.entry.js +1 -1
  58. package/dist/cjs/utrecht-pagination.cjs.entry.js +1 -1
  59. package/dist/cjs/utrecht-separator.cjs.entry.js +1 -1
  60. package/dist/cjs/utrecht-sidenav.cjs.entry.js +1 -1
  61. package/dist/cjs/utrecht-textbox.cjs.entry.js +1 -1
  62. package/dist/cjs/utrecht.cjs.js +3 -3
  63. package/dist/collection/backdrop/bem.js +12 -0
  64. package/dist/collection/backdrop/checkerboard.js +20 -0
  65. package/dist/collection/backdrop/stencil.css +33 -0
  66. package/dist/collection/backdrop/stencil.js +40 -0
  67. package/dist/collection/collection-manifest.json +3 -2
  68. package/dist/collection/node_modules/svgo/dist/svgo.browser.js +1 -1
  69. package/dist/collection/node_modules/svgo/lib/stringifier.js +326 -0
  70. package/dist/collection/node_modules/svgo/lib/svgo/coa.js +23 -21
  71. package/dist/collection/node_modules/svgo/lib/svgo/plugins.js +16 -0
  72. package/dist/collection/node_modules/svgo/lib/svgo-node.js +21 -14
  73. package/dist/collection/node_modules/svgo/lib/svgo.js +2 -5
  74. package/dist/collection/node_modules/svgo/plugins/inlineStyles.js +334 -259
  75. package/dist/collection/node_modules/svgo/plugins/prefixIds.js +9 -8
  76. package/dist/collection/node_modules/svgo/plugins/removeEmptyAttrs.js +19 -19
  77. package/dist/collection/node_modules/svgo/plugins/removeXMLNS.js +1 -0
  78. package/dist/custom-elements/index.d.ts +6 -0
  79. package/dist/custom-elements/index.js +121 -104
  80. package/dist/esm/{index-d3aa9623.js → index-dbad55a3.js} +67 -58
  81. package/dist/esm/loader.js +3 -3
  82. package/dist/esm/utrecht-article.entry.js +1 -1
  83. package/dist/esm/utrecht-backdrop.entry.js +17 -0
  84. package/dist/esm/utrecht-badge-counter.entry.js +1 -1
  85. package/dist/esm/utrecht-badge-data.entry.js +1 -1
  86. package/dist/esm/utrecht-badge-status.entry.js +1 -1
  87. package/dist/esm/utrecht-breadcrumb.entry.js +1 -1
  88. package/dist/esm/utrecht-button_2.entry.js +1 -1
  89. package/dist/esm/utrecht-checkbox.entry.js +1 -1
  90. package/dist/esm/utrecht-contact-card-template.entry.js +1 -1
  91. package/dist/esm/utrecht-custom-checkbox.entry.js +1 -1
  92. package/dist/esm/utrecht-digid-button.entry.js +1 -1
  93. package/dist/esm/utrecht-document.entry.js +1 -1
  94. package/dist/esm/utrecht-eherkenning-logo.entry.js +1 -1
  95. package/dist/esm/utrecht-eidas-logo.entry.js +1 -1
  96. package/dist/esm/utrecht-form-field-checkbox.entry.js +1 -1
  97. package/dist/esm/utrecht-form-field-description.entry.js +1 -1
  98. package/dist/esm/utrecht-form-field-textarea.entry.js +1 -1
  99. package/dist/esm/utrecht-form-field-textbox.entry.js +1 -1
  100. package/dist/esm/utrecht-form-toggle.entry.js +1 -1
  101. package/dist/esm/utrecht-heading-1.entry.js +1 -1
  102. package/dist/esm/utrecht-heading-2_3.entry.js +1 -1
  103. package/dist/esm/utrecht-heading-4.entry.js +1 -1
  104. package/dist/esm/utrecht-heading-5.entry.js +1 -1
  105. package/dist/esm/utrecht-heading-6.entry.js +1 -1
  106. package/dist/esm/utrecht-heading.entry.js +1 -1
  107. package/dist/esm/utrecht-html-content.entry.js +1 -1
  108. package/dist/esm/utrecht-icon-afval.entry.js +1 -1
  109. package/dist/esm/utrecht-icon-arrow.entry.js +1 -1
  110. package/dist/esm/utrecht-icon-checkmark.entry.js +1 -1
  111. package/dist/esm/utrecht-icon-cross.entry.js +1 -1
  112. package/dist/esm/utrecht-icon-error.entry.js +1 -1
  113. package/dist/esm/utrecht-icon-facebook.entry.js +1 -1
  114. package/dist/esm/utrecht-icon-filter.entry.js +1 -1
  115. package/dist/esm/utrecht-icon-information.entry.js +1 -1
  116. package/dist/esm/utrecht-icon-instagram.entry.js +1 -1
  117. package/dist/esm/utrecht-icon-linkedin.entry.js +1 -1
  118. package/dist/esm/utrecht-icon-list.entry.js +1 -1
  119. package/dist/esm/utrecht-icon-loupe.entry.js +1 -1
  120. package/dist/esm/utrecht-icon-melding.entry.js +1 -1
  121. package/dist/esm/utrecht-icon-paspoort.entry.js +1 -1
  122. package/dist/esm/utrecht-icon-rijbewijs.entry.js +1 -1
  123. package/dist/esm/utrecht-icon-toeslag.entry.js +1 -1
  124. package/dist/esm/utrecht-icon-trouwen.entry.js +1 -1
  125. package/dist/esm/utrecht-icon-twitter.entry.js +1 -1
  126. package/dist/esm/utrecht-icon-verhuizen.entry.js +1 -1
  127. package/dist/esm/utrecht-icon-warning.entry.js +1 -1
  128. package/dist/esm/utrecht-icon-whatsapp.entry.js +1 -1
  129. package/dist/esm/utrecht-icon-zoomin.entry.js +1 -1
  130. package/dist/esm/utrecht-icon-zoomout.entry.js +1 -1
  131. package/dist/esm/utrecht-logo-button.entry.js +1 -1
  132. package/dist/esm/utrecht-logo.entry.js +1 -1
  133. package/dist/esm/utrecht-page-content.entry.js +1 -1
  134. package/dist/esm/utrecht-page-footer.entry.js +1 -1
  135. package/dist/esm/utrecht-page-header.entry.js +1 -1
  136. package/dist/esm/utrecht-page.entry.js +1 -1
  137. package/dist/esm/utrecht-pagination.entry.js +1 -1
  138. package/dist/esm/utrecht-separator.entry.js +1 -1
  139. package/dist/esm/utrecht-sidenav.entry.js +1 -1
  140. package/dist/esm/utrecht-textbox.entry.js +1 -1
  141. package/dist/esm/utrecht.js +3 -3
  142. package/dist/types/backdrop/stencil.d.ts +4 -0
  143. package/dist/types/components.d.ts +15 -0
  144. package/dist/utrecht/{p-80bf3205.entry.js → p-06d4e8da.entry.js} +1 -1
  145. package/dist/utrecht/{p-354528c0.entry.js → p-0e908b3b.entry.js} +1 -1
  146. package/dist/utrecht/{p-b9c4ab78.entry.js → p-12d3555b.entry.js} +1 -1
  147. package/dist/utrecht/{p-47c389d4.entry.js → p-180ccec7.entry.js} +1 -1
  148. package/dist/utrecht/{p-65f6e516.entry.js → p-19697551.entry.js} +1 -1
  149. package/dist/utrecht/{p-9c64026a.entry.js → p-19bb7aea.entry.js} +1 -1
  150. package/dist/utrecht/{p-4518d0f3.entry.js → p-1b369d6f.entry.js} +1 -1
  151. package/dist/utrecht/{p-09027cac.entry.js → p-2038f779.entry.js} +1 -1
  152. package/dist/utrecht/{p-2df1e2ae.entry.js → p-21b832ea.entry.js} +1 -1
  153. package/dist/utrecht/{p-1e7c7239.entry.js → p-29a17a39.entry.js} +1 -1
  154. package/dist/utrecht/{p-9703f352.entry.js → p-2be490d7.entry.js} +1 -1
  155. package/dist/utrecht/{p-5638a44d.entry.js → p-2ff6bfbb.entry.js} +1 -1
  156. package/dist/utrecht/{p-c856c9ea.entry.js → p-3672cbd0.entry.js} +1 -1
  157. package/dist/utrecht/{p-e1ff4f2f.entry.js → p-3bda190e.entry.js} +1 -1
  158. package/dist/utrecht/{p-0fb968c5.entry.js → p-401d2b63.entry.js} +1 -1
  159. package/dist/utrecht/{p-2be17af5.entry.js → p-45db7b13.entry.js} +1 -1
  160. package/dist/utrecht/{p-5e75593e.entry.js → p-49a2e309.entry.js} +1 -1
  161. package/dist/utrecht/{p-dad4cd1d.entry.js → p-585963b2.entry.js} +1 -1
  162. package/dist/utrecht/{p-8020e5d3.entry.js → p-5eec66c0.entry.js} +1 -1
  163. package/dist/utrecht/{p-8944aac6.entry.js → p-5f114e24.entry.js} +1 -1
  164. package/dist/utrecht/{p-09b6b28b.entry.js → p-60321853.entry.js} +1 -1
  165. package/dist/utrecht/{p-cd249af1.entry.js → p-673512bf.entry.js} +1 -1
  166. package/dist/utrecht/{p-a73c6a4a.entry.js → p-6922d6e5.entry.js} +1 -1
  167. package/dist/utrecht/{p-1332795f.entry.js → p-6a07112a.entry.js} +1 -1
  168. package/dist/utrecht/{p-a4bc0835.entry.js → p-6aec0d5e.entry.js} +1 -1
  169. package/dist/utrecht/{p-2d57d061.entry.js → p-72670926.entry.js} +1 -1
  170. package/dist/utrecht/{p-5961e7e7.entry.js → p-7468ce00.entry.js} +1 -1
  171. package/dist/utrecht/{p-ad1609ac.entry.js → p-8075a496.entry.js} +1 -1
  172. package/dist/utrecht/{p-dfe6f633.entry.js → p-84481a4b.entry.js} +1 -1
  173. package/dist/utrecht/{p-3f06bf41.entry.js → p-8856bd84.entry.js} +1 -1
  174. package/dist/utrecht/{p-28bc1d4c.entry.js → p-88e09f6e.entry.js} +1 -1
  175. package/dist/utrecht/{p-177f47d2.entry.js → p-99d68ed5.entry.js} +1 -1
  176. package/dist/utrecht/{p-a5f53756.entry.js → p-9a7712ee.entry.js} +1 -1
  177. package/dist/utrecht/{p-bb949c54.entry.js → p-9bde6988.entry.js} +1 -1
  178. package/dist/utrecht/p-a41d30a7.js +1 -0
  179. package/dist/utrecht/{p-74336ae3.entry.js → p-a62dbf0b.entry.js} +1 -1
  180. package/dist/utrecht/{p-c1dd8bb0.entry.js → p-a82cf768.entry.js} +1 -1
  181. package/dist/utrecht/{p-0d99e925.entry.js → p-b0860b62.entry.js} +1 -1
  182. package/dist/utrecht/{p-7d89d47a.entry.js → p-b3aa468d.entry.js} +1 -1
  183. package/dist/utrecht/{p-7af4fa6d.entry.js → p-bedc995b.entry.js} +1 -1
  184. package/dist/utrecht/{p-31dcf33f.entry.js → p-c22f500b.entry.js} +1 -1
  185. package/dist/utrecht/{p-57a8d82c.entry.js → p-c314b6c1.entry.js} +1 -1
  186. package/dist/utrecht/{p-8bf10ece.entry.js → p-c7f7af31.entry.js} +1 -1
  187. package/dist/utrecht/{p-b20e7333.entry.js → p-c948bdeb.entry.js} +1 -1
  188. package/dist/utrecht/{p-40e0a9ed.entry.js → p-ccd0835e.entry.js} +1 -1
  189. package/dist/utrecht/{p-d8c61464.entry.js → p-d21af965.entry.js} +1 -1
  190. package/dist/utrecht/{p-42f7a543.entry.js → p-d24e72f1.entry.js} +1 -1
  191. package/dist/utrecht/{p-4ae308fc.entry.js → p-d5cec830.entry.js} +1 -1
  192. package/dist/utrecht/{p-2b3fe7c2.entry.js → p-e094341c.entry.js} +1 -1
  193. package/dist/utrecht/{p-685733e4.entry.js → p-e1ab41a2.entry.js} +1 -1
  194. package/dist/utrecht/{p-df7db7e9.entry.js → p-e8d0b7bf.entry.js} +1 -1
  195. package/dist/utrecht/{p-2ffbac3c.entry.js → p-e8f6c846.entry.js} +1 -1
  196. package/dist/utrecht/{p-9455dd17.entry.js → p-e9ae2a68.entry.js} +1 -1
  197. package/dist/utrecht/{p-d74b5f06.entry.js → p-ea6f5e30.entry.js} +1 -1
  198. package/dist/utrecht/{p-afa2eae0.entry.js → p-f0cea495.entry.js} +1 -1
  199. package/dist/utrecht/{p-bcc63ada.entry.js → p-f1f0a2c6.entry.js} +1 -1
  200. package/dist/utrecht/{p-82698d3b.entry.js → p-f2792d17.entry.js} +1 -1
  201. package/dist/utrecht/{p-faefee5f.entry.js → p-f47ed55e.entry.js} +1 -1
  202. package/dist/utrecht/p-f9337bd7.entry.js +1 -0
  203. package/dist/utrecht/{p-bea27e6e.entry.js → p-fb80f09f.entry.js} +1 -1
  204. package/dist/utrecht/utrecht.esm.js +1 -1
  205. package/package.json +4 -4
  206. package/dist/collection/node_modules/svgo/lib/svgo/js2svg.js +0 -337
  207. package/dist/utrecht/p-821ca495.js +0 -1
@@ -1,24 +1,43 @@
1
1
  'use strict';
2
2
 
3
- const csstree = require('css-tree');
4
- const { querySelectorAll, closestByName } = require('../lib/xast.js');
5
- const cssTools = require('../lib/css-tools');
3
+ /**
4
+ * @typedef {import('../lib/types').Specificity} Specificity
5
+ * @typedef {import('../lib/types').XastElement} XastElement
6
+ * @typedef {import('../lib/types').XastParent} XastParent
7
+ */
6
8
 
9
+ const csstree = require('css-tree');
10
+ // @ts-ignore not defined in @types/csso
11
+ const specificity = require('csso/lib/restructure/prepare/specificity');
12
+ const stable = require('stable');
13
+ const {
14
+ visitSkip,
15
+ querySelectorAll,
16
+ detachNodeFromParent,
17
+ } = require('../lib/xast.js');
18
+
19
+ exports.type = 'visitor';
7
20
  exports.name = 'inlineStyles';
8
-
9
- exports.type = 'full';
10
-
11
21
  exports.active = true;
22
+ exports.description = 'inline styles (additional options)';
12
23
 
13
- exports.params = {
14
- onlyMatchedOnce: true,
15
- removeMatchedSelectors: true,
16
- useMqs: ['', 'screen'],
17
- usePseudos: [''],
24
+ /**
25
+ * Compares two selector specificities.
26
+ * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
27
+ *
28
+ * @type {(a: Specificity, b: Specificity) => number}
29
+ */
30
+ const compareSpecificity = (a, b) => {
31
+ for (var i = 0; i < 4; i += 1) {
32
+ if (a[i] < b[i]) {
33
+ return -1;
34
+ } else if (a[i] > b[i]) {
35
+ return 1;
36
+ }
37
+ }
38
+ return 0;
18
39
  };
19
40
 
20
- exports.description = 'inline styles (additional options)';
21
-
22
41
  /**
23
42
  * Moves + merges styles from style elements to element styles
24
43
  *
@@ -38,267 +57,323 @@ exports.description = 'inline styles (additional options)';
38
57
  * what pseudo-classes/-elements to be used
39
58
  * empty string element for all non-pseudo-classes and/or -elements
40
59
  *
41
- * @param {Object} root document element
42
- * @param {Object} opts plugin params
43
- *
44
60
  * @author strarsis <strarsis@gmail.com>
61
+ *
62
+ * @type {import('../lib/types').Plugin<{
63
+ * onlyMatchedOnce?: boolean,
64
+ * removeMatchedSelectors?: boolean,
65
+ * useMqs?: Array<string>,
66
+ * usePseudos?: Array<string>
67
+ * }>}
45
68
  */
46
- exports.fn = function (root, opts) {
47
- // collect <style/>s
48
- var styleEls = querySelectorAll(root, 'style');
49
-
50
- //no <styles/>s, nothing to do
51
- if (styleEls.length === 0) {
52
- return root;
53
- }
54
-
55
- var styles = [],
56
- selectors = [];
57
-
58
- for (var styleEl of styleEls) {
59
- // values other than the empty string or text/css are not used
60
- if (
61
- styleEl.attributes.type != null &&
62
- styleEl.attributes.type !== '' &&
63
- styleEl.attributes.type !== 'text/css'
64
- ) {
65
- continue;
66
- }
67
- // skip empty <style/>s or <foreignObject> content.
68
- if (
69
- styleEl.children.length === 0 ||
70
- closestByName(styleEl, 'foreignObject')
71
- ) {
72
- continue;
73
- }
74
-
75
- var cssStr = cssTools.getCssStr(styleEl);
76
-
77
- // collect <style/>s and their css ast
78
- var cssAst = {};
79
- try {
80
- cssAst = csstree.parse(cssStr, {
81
- parseValue: false,
82
- parseCustomProperty: false,
83
- });
84
- } catch (parseError) {
85
- // console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError);
86
- continue;
87
- }
88
-
89
- styles.push({
90
- styleEl: styleEl,
91
- cssAst: cssAst,
92
- });
93
-
94
- selectors = selectors.concat(cssTools.flattenToSelectors(cssAst));
95
- }
96
-
97
- // filter for mediaqueries to be used or without any mediaquery
98
- var selectorsMq = cssTools.filterByMqs(selectors, opts.useMqs);
99
-
100
- // filter for pseudo elements to be used
101
- var selectorsPseudo = cssTools.filterByPseudos(selectorsMq, opts.usePseudos);
102
-
103
- // remove PseudoClass from its SimpleSelector for proper matching
104
- cssTools.cleanPseudos(selectorsPseudo);
105
-
106
- // stable sort selectors
107
- var sortedSelectors = cssTools.sortSelectors(selectorsPseudo).reverse();
108
-
109
- var selector, selectedEl;
110
-
111
- // match selectors
112
- for (selector of sortedSelectors) {
113
- var selectorStr = csstree.generate(selector.item.data),
114
- selectedEls = null;
115
-
116
- try {
117
- selectedEls = querySelectorAll(root, selectorStr);
118
- } catch (selectError) {
119
- // console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError);
120
- continue;
121
- }
122
-
123
- if (selectedEls.length === 0) {
124
- // nothing selected
125
- continue;
126
- }
127
-
128
- selector.selectedEls = selectedEls;
129
- }
130
-
131
- // apply <style/> styles to matched elements
132
- for (selector of sortedSelectors) {
133
- if (!selector.selectedEls) {
134
- continue;
135
- }
136
-
137
- if (
138
- opts.onlyMatchedOnce &&
139
- selector.selectedEls !== null &&
140
- selector.selectedEls.length > 1
141
- ) {
142
- // skip selectors that match more than once if option onlyMatchedOnce is enabled
143
- continue;
144
- }
145
-
146
- // apply <style/> to matched elements
147
- for (selectedEl of selector.selectedEls) {
148
- if (selector.rule === null) {
149
- continue;
150
- }
151
- const styleDeclarationList = csstree.parse(
152
- selectedEl.attributes.style == null ? '' : selectedEl.attributes.style,
153
- {
154
- context: 'declarationList',
155
- parseValue: false,
69
+ exports.fn = (root, params) => {
70
+ const {
71
+ onlyMatchedOnce = true,
72
+ removeMatchedSelectors = true,
73
+ useMqs = ['', 'screen'],
74
+ usePseudos = [''],
75
+ } = params;
76
+
77
+ /**
78
+ * @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>}
79
+ */
80
+ const styles = [];
81
+ /**
82
+ * @type {Array<{
83
+ * node: csstree.Selector,
84
+ * item: csstree.ListItem<csstree.CssNode>,
85
+ * rule: csstree.Rule,
86
+ * matchedElements?: Array<XastElement>
87
+ * }>}
88
+ */
89
+ let selectors = [];
90
+
91
+ return {
92
+ element: {
93
+ enter: (node, parentNode) => {
94
+ // skip <foreignObject /> content
95
+ if (node.name === 'foreignObject') {
96
+ return visitSkip;
156
97
  }
157
- );
158
- const styleDeclarationItems = new Map();
159
- csstree.walk(styleDeclarationList, {
160
- visit: 'Declaration',
161
- enter(node, item) {
162
- styleDeclarationItems.set(node.property, item);
163
- },
164
- });
165
- // merge declarations
166
- csstree.walk(selector.rule, {
167
- visit: 'Declaration',
168
- enter(ruleDeclaration) {
169
- // existing inline styles have higher priority
170
- // no inline styles, external styles, external styles used
171
- // inline styles, external styles same priority as inline styles, inline styles used
172
- // inline styles, external styles higher priority than inline styles, external styles used
173
- const matchedItem = styleDeclarationItems.get(
174
- ruleDeclaration.property
175
- );
176
- const ruleDeclarationItem =
177
- styleDeclarationList.children.createItem(ruleDeclaration);
178
- if (matchedItem == null) {
179
- styleDeclarationList.children.append(ruleDeclarationItem);
180
- } else if (
181
- matchedItem.data.important !== true &&
182
- ruleDeclaration.important === true
183
- ) {
184
- styleDeclarationList.children.replace(
185
- matchedItem,
186
- ruleDeclarationItem
187
- );
188
- styleDeclarationItems.set(
189
- ruleDeclaration.property,
190
- ruleDeclarationItem
191
- );
98
+ // collect only non-empty <style /> elements
99
+ if (node.name !== 'style' || node.children.length === 0) {
100
+ return;
101
+ }
102
+ // values other than the empty string or text/css are not used
103
+ if (
104
+ node.attributes.type != null &&
105
+ node.attributes.type !== '' &&
106
+ node.attributes.type !== 'text/css'
107
+ ) {
108
+ return;
109
+ }
110
+ // parse css in style element
111
+ let cssText = '';
112
+ for (const child of node.children) {
113
+ if (child.type === 'text' || child.type === 'cdata') {
114
+ cssText += child.value;
192
115
  }
193
- },
194
- });
195
- selectedEl.attributes.style = csstree.generate(styleDeclarationList);
196
- }
197
-
198
- if (
199
- opts.removeMatchedSelectors &&
200
- selector.selectedEls !== null &&
201
- selector.selectedEls.length > 0
202
- ) {
203
- // clean up matching simple selectors if option removeMatchedSelectors is enabled
204
- selector.rule.prelude.children.remove(selector.item);
205
- }
206
- }
116
+ }
117
+ /**
118
+ * @type {null | csstree.CssNode}
119
+ */
120
+ let cssAst = null;
121
+ try {
122
+ cssAst = csstree.parse(cssText, {
123
+ parseValue: false,
124
+ parseCustomProperty: false,
125
+ });
126
+ } catch {
127
+ return;
128
+ }
129
+ if (cssAst.type === 'StyleSheet') {
130
+ styles.push({ node, parentNode, cssAst });
131
+ }
207
132
 
208
- if (!opts.removeMatchedSelectors) {
209
- return root; // no further processing required
210
- }
133
+ // collect selectors
134
+ csstree.walk(cssAst, {
135
+ visit: 'Selector',
136
+ enter(node, item) {
137
+ const atrule = this.atrule;
138
+ const rule = this.rule;
139
+ if (rule == null) {
140
+ return;
141
+ }
142
+
143
+ // skip media queries not included into useMqs param
144
+ let mq = '';
145
+ if (atrule != null) {
146
+ mq = atrule.name;
147
+ if (atrule.prelude != null) {
148
+ mq += ` ${csstree.generate(atrule.prelude)}`;
149
+ }
150
+ }
151
+ if (useMqs.includes(mq) === false) {
152
+ return;
153
+ }
154
+
155
+ /**
156
+ * @type {Array<{
157
+ * item: csstree.ListItem<csstree.CssNode>,
158
+ * list: csstree.List<csstree.CssNode>
159
+ * }>}
160
+ */
161
+ const pseudos = [];
162
+ if (node.type === 'Selector') {
163
+ node.children.each((childNode, childItem, childList) => {
164
+ if (
165
+ childNode.type === 'PseudoClassSelector' ||
166
+ childNode.type === 'PseudoElementSelector'
167
+ ) {
168
+ pseudos.push({ item: childItem, list: childList });
169
+ }
170
+ });
171
+ }
172
+
173
+ // skip pseudo classes and pseudo elements not includes into usePseudos param
174
+ const pseudoSelectors = csstree.generate({
175
+ type: 'Selector',
176
+ children: new csstree.List().fromArray(
177
+ pseudos.map((pseudo) => pseudo.item.data)
178
+ ),
179
+ });
180
+ if (usePseudos.includes(pseudoSelectors) === false) {
181
+ return;
182
+ }
183
+
184
+ // remove pseudo classes and elements to allow querySelector match elements
185
+ // TODO this is not very accurate since some pseudo classes like first-child
186
+ // are used for selection
187
+ for (const pseudo of pseudos) {
188
+ pseudo.list.remove(pseudo.item);
189
+ }
190
+
191
+ selectors.push({ node, item, rule });
192
+ },
193
+ });
194
+ },
195
+ },
211
196
 
212
- // clean up matched class + ID attribute values
213
- for (selector of sortedSelectors) {
214
- if (!selector.selectedEls) {
215
- continue;
216
- }
197
+ root: {
198
+ exit: () => {
199
+ if (styles.length === 0) {
200
+ return;
201
+ }
202
+ // stable sort selectors
203
+ const sortedSelectors = stable(selectors, (a, b) => {
204
+ const aSpecificity = specificity(a.item.data);
205
+ const bSpecificity = specificity(b.item.data);
206
+ return compareSpecificity(aSpecificity, bSpecificity);
207
+ }).reverse();
208
+
209
+ for (const selector of sortedSelectors) {
210
+ // match selectors
211
+ const selectorText = csstree.generate(selector.item.data);
212
+ /**
213
+ * @type {Array<XastElement>}
214
+ */
215
+ const matchedElements = [];
216
+ try {
217
+ for (const node of querySelectorAll(root, selectorText)) {
218
+ if (node.type === 'element') {
219
+ matchedElements.push(node);
220
+ }
221
+ }
222
+ } catch (selectError) {
223
+ continue;
224
+ }
225
+ // nothing selected
226
+ if (matchedElements.length === 0) {
227
+ continue;
228
+ }
217
229
 
218
- if (
219
- opts.onlyMatchedOnce &&
220
- selector.selectedEls !== null &&
221
- selector.selectedEls.length > 1
222
- ) {
223
- // skip selectors that match more than once if option onlyMatchedOnce is enabled
224
- continue;
225
- }
230
+ // apply styles to matched elements
231
+ // skip selectors that match more than once if option onlyMatchedOnce is enabled
232
+ if (onlyMatchedOnce && matchedElements.length > 1) {
233
+ continue;
234
+ }
226
235
 
227
- for (selectedEl of selector.selectedEls) {
228
- // class
229
- const classList = new Set(
230
- selectedEl.attributes.class == null
231
- ? null
232
- : selectedEl.attributes.class.split(' ')
233
- );
234
- const firstSubSelector = selector.item.data.children.first();
235
- if (firstSubSelector.type === 'ClassSelector') {
236
- classList.delete(firstSubSelector.name);
237
- }
238
- if (classList.size === 0) {
239
- delete selectedEl.attributes.class;
240
- } else {
241
- selectedEl.attributes.class = Array.from(classList).join(' ');
242
- }
236
+ // apply <style/> to matched elements
237
+ for (const selectedEl of matchedElements) {
238
+ const styleDeclarationList = csstree.parse(
239
+ selectedEl.attributes.style == null
240
+ ? ''
241
+ : selectedEl.attributes.style,
242
+ {
243
+ context: 'declarationList',
244
+ parseValue: false,
245
+ }
246
+ );
247
+ if (styleDeclarationList.type !== 'DeclarationList') {
248
+ continue;
249
+ }
250
+ const styleDeclarationItems = new Map();
251
+ csstree.walk(styleDeclarationList, {
252
+ visit: 'Declaration',
253
+ enter(node, item) {
254
+ styleDeclarationItems.set(node.property, item);
255
+ },
256
+ });
257
+ // merge declarations
258
+ csstree.walk(selector.rule, {
259
+ visit: 'Declaration',
260
+ enter(ruleDeclaration) {
261
+ // existing inline styles have higher priority
262
+ // no inline styles, external styles, external styles used
263
+ // inline styles, external styles same priority as inline styles, inline styles used
264
+ // inline styles, external styles higher priority than inline styles, external styles used
265
+ const matchedItem = styleDeclarationItems.get(
266
+ ruleDeclaration.property
267
+ );
268
+ const ruleDeclarationItem =
269
+ styleDeclarationList.children.createItem(ruleDeclaration);
270
+ if (matchedItem == null) {
271
+ styleDeclarationList.children.append(ruleDeclarationItem);
272
+ } else if (
273
+ matchedItem.data.important !== true &&
274
+ ruleDeclaration.important === true
275
+ ) {
276
+ styleDeclarationList.children.replace(
277
+ matchedItem,
278
+ ruleDeclarationItem
279
+ );
280
+ styleDeclarationItems.set(
281
+ ruleDeclaration.property,
282
+ ruleDeclarationItem
283
+ );
284
+ }
285
+ },
286
+ });
287
+ selectedEl.attributes.style =
288
+ csstree.generate(styleDeclarationList);
289
+ }
243
290
 
244
- // ID
245
- if (firstSubSelector.type === 'IdSelector') {
246
- if (selectedEl.attributes.id === firstSubSelector.name) {
247
- delete selectedEl.attributes.id;
291
+ if (
292
+ removeMatchedSelectors &&
293
+ matchedElements.length !== 0 &&
294
+ selector.rule.prelude.type === 'SelectorList'
295
+ ) {
296
+ // clean up matching simple selectors if option removeMatchedSelectors is enabled
297
+ selector.rule.prelude.children.remove(selector.item);
298
+ }
299
+ selector.matchedElements = matchedElements;
248
300
  }
249
- }
250
- }
251
- }
252
301
 
253
- // clean up now empty elements
254
- for (var style of styles) {
255
- csstree.walk(style.cssAst, {
256
- visit: 'Rule',
257
- enter: function (node, item, list) {
258
- // clean up <style/> atrules without any rulesets left
259
- if (
260
- node.type === 'Atrule' &&
261
- // only Atrules containing rulesets
262
- node.block !== null &&
263
- node.block.children.isEmpty()
264
- ) {
265
- list.remove(item);
302
+ // no further processing required
303
+ if (removeMatchedSelectors === false) {
266
304
  return;
267
305
  }
268
306
 
269
- // clean up <style/> rulesets without any css selectors left
270
- if (node.type === 'Rule' && node.prelude.children.isEmpty()) {
271
- list.remove(item);
272
- }
273
- },
274
- });
275
-
276
- if (style.cssAst.children.isEmpty()) {
277
- // clean up now emtpy <style/>s
278
- var styleParentEl = style.styleEl.parentNode;
279
- styleParentEl.spliceContent(
280
- styleParentEl.children.indexOf(style.styleEl),
281
- 1
282
- );
283
-
284
- if (
285
- styleParentEl.name === 'defs' &&
286
- styleParentEl.children.length === 0
287
- ) {
288
- // also clean up now empty <def/>s
289
- var defsParentEl = styleParentEl.parentNode;
290
- defsParentEl.spliceContent(
291
- defsParentEl.children.indexOf(styleParentEl),
292
- 1
293
- );
294
- }
307
+ // clean up matched class + ID attribute values
308
+ for (const selector of sortedSelectors) {
309
+ if (selector.matchedElements == null) {
310
+ continue;
311
+ }
295
312
 
296
- continue;
297
- }
313
+ if (onlyMatchedOnce && selector.matchedElements.length > 1) {
314
+ // skip selectors that match more than once if option onlyMatchedOnce is enabled
315
+ continue;
316
+ }
298
317
 
299
- // update existing, left over <style>s
300
- cssTools.setCssStr(style.styleEl, csstree.generate(style.cssAst));
301
- }
318
+ for (const selectedEl of selector.matchedElements) {
319
+ // class
320
+ const classList = new Set(
321
+ selectedEl.attributes.class == null
322
+ ? null
323
+ : selectedEl.attributes.class.split(' ')
324
+ );
325
+ const firstSubSelector = selector.node.children.first();
326
+ if (
327
+ firstSubSelector != null &&
328
+ firstSubSelector.type === 'ClassSelector'
329
+ ) {
330
+ classList.delete(firstSubSelector.name);
331
+ }
332
+ if (classList.size === 0) {
333
+ delete selectedEl.attributes.class;
334
+ } else {
335
+ selectedEl.attributes.class = Array.from(classList).join(' ');
336
+ }
337
+
338
+ // ID
339
+ if (
340
+ firstSubSelector != null &&
341
+ firstSubSelector.type === 'IdSelector'
342
+ ) {
343
+ if (selectedEl.attributes.id === firstSubSelector.name) {
344
+ delete selectedEl.attributes.id;
345
+ }
346
+ }
347
+ }
348
+ }
302
349
 
303
- return root;
350
+ for (const style of styles) {
351
+ csstree.walk(style.cssAst, {
352
+ visit: 'Rule',
353
+ enter: function (node, item, list) {
354
+ // clean up <style/> rulesets without any css selectors left
355
+ if (
356
+ node.type === 'Rule' &&
357
+ node.prelude.type === 'SelectorList' &&
358
+ node.prelude.children.isEmpty()
359
+ ) {
360
+ list.remove(item);
361
+ }
362
+ },
363
+ });
364
+
365
+ if (style.cssAst.children.isEmpty()) {
366
+ // remove emtpy style element
367
+ detachNodeFromParent(style.node, style.parentNode);
368
+ } else {
369
+ // update style element if any styles left
370
+ const firstChild = style.node.children[0];
371
+ if (firstChild.type === 'text' || firstChild.type === 'cdata') {
372
+ firstChild.value = csstree.generate(style.cssAst);
373
+ }
374
+ }
375
+ }
376
+ },
377
+ },
378
+ };
304
379
  };
@@ -206,15 +206,16 @@ exports.fn = (_root, params, info) => {
206
206
  node.attributes[name] != null &&
207
207
  node.attributes[name].length !== 0
208
208
  ) {
209
- // extract id reference from url(...) value
210
- const matches = /url\((.*?)\)/gi.exec(node.attributes[name]);
211
- if (matches != null) {
212
- const value = matches[1];
213
- const prefixed = prefixReference(prefix, value);
214
- if (prefixed != null) {
215
- node.attributes[name] = `url(${prefixed})`;
209
+ node.attributes[name] = node.attributes[name].replace(
210
+ /url\((.*?)\)/gi,
211
+ (match, url) => {
212
+ const prefixed = prefixReference(prefix, url);
213
+ if (prefixed == null) {
214
+ return match;
215
+ }
216
+ return `url(${prefixed})`;
216
217
  }
217
- }
218
+ );
218
219
  }
219
220
  }
220
221