removeAttrs.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. 'use strict';
  2. exports.name = 'removeAttrs';
  3. exports.type = 'visitor';
  4. exports.active = false;
  5. exports.description = 'removes specified attributes';
  6. const DEFAULT_SEPARATOR = ':';
  7. const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.
  8. It should have a pattern to remove, otherwise the plugin is a noop.
  9. Config example:
  10. plugins: [
  11. {
  12. name: "removeAttrs",
  13. params: {
  14. attrs: "(fill|stroke)"
  15. }
  16. }
  17. ]
  18. `;
  19. /**
  20. * Remove attributes
  21. *
  22. * @example elemSeparator
  23. * format: string
  24. *
  25. * @example preserveCurrentColor
  26. * format: boolean
  27. *
  28. * @example attrs:
  29. *
  30. * format: [ element* : attribute* : value* ]
  31. *
  32. * element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used)
  33. * attribute : regexp (wrapped into ^...$)
  34. * value : regexp (wrapped into ^...$), single * or omitted > all values
  35. *
  36. * examples:
  37. *
  38. * > basic: remove fill attribute
  39. * ---
  40. * removeAttrs:
  41. * attrs: 'fill'
  42. *
  43. * > remove fill attribute on path element
  44. * ---
  45. * attrs: 'path:fill'
  46. *
  47. * > remove fill attribute on path element where value is none
  48. * ---
  49. * attrs: 'path:fill:none'
  50. *
  51. *
  52. * > remove all fill and stroke attribute
  53. * ---
  54. * attrs:
  55. * - 'fill'
  56. * - 'stroke'
  57. *
  58. * [is same as]
  59. *
  60. * attrs: '(fill|stroke)'
  61. *
  62. * [is same as]
  63. *
  64. * attrs: '*:(fill|stroke)'
  65. *
  66. * [is same as]
  67. *
  68. * attrs: '.*:(fill|stroke)'
  69. *
  70. * [is same as]
  71. *
  72. * attrs: '.*:(fill|stroke):.*'
  73. *
  74. *
  75. * > remove all stroke related attributes
  76. * ----
  77. * attrs: 'stroke.*'
  78. *
  79. *
  80. * @author Benny Schudel
  81. *
  82. * @type {import('../lib/types').Plugin<{
  83. * elemSeparator?: string,
  84. * preserveCurrentColor?: boolean,
  85. * attrs: string | Array<string>
  86. * }>}
  87. */
  88. exports.fn = (root, params) => {
  89. if (typeof params.attrs == 'undefined') {
  90. console.warn(ENOATTRS);
  91. return null;
  92. }
  93. const elemSeparator =
  94. typeof params.elemSeparator == 'string'
  95. ? params.elemSeparator
  96. : DEFAULT_SEPARATOR;
  97. const preserveCurrentColor =
  98. typeof params.preserveCurrentColor == 'boolean'
  99. ? params.preserveCurrentColor
  100. : false;
  101. const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs];
  102. return {
  103. element: {
  104. enter: (node) => {
  105. for (let pattern of attrs) {
  106. // if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value*
  107. if (pattern.includes(elemSeparator) === false) {
  108. pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join(
  109. ''
  110. );
  111. // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value
  112. } else if (pattern.split(elemSeparator).length < 3) {
  113. pattern = [pattern, elemSeparator, '.*'].join('');
  114. }
  115. // create regexps for element, attribute name, and attribute value
  116. const list = pattern.split(elemSeparator).map((value) => {
  117. // adjust single * to match anything
  118. if (value === '*') {
  119. value = '.*';
  120. }
  121. return new RegExp(['^', value, '$'].join(''), 'i');
  122. });
  123. // matches element
  124. if (list[0].test(node.name)) {
  125. // loop attributes
  126. for (const [name, value] of Object.entries(node.attributes)) {
  127. const isFillCurrentColor =
  128. preserveCurrentColor &&
  129. name == 'fill' &&
  130. value == 'currentColor';
  131. const isStrokeCurrentColor =
  132. preserveCurrentColor &&
  133. name == 'stroke' &&
  134. value == 'currentColor';
  135. if (
  136. !isFillCurrentColor &&
  137. !isStrokeCurrentColor &&
  138. // matches attribute name
  139. list[1].test(name) &&
  140. // matches attribute value
  141. list[2].test(value)
  142. ) {
  143. delete node.attributes[name];
  144. }
  145. }
  146. }
  147. }
  148. },
  149. },
  150. };
  151. };