'use strict'; exports.name = 'removeAttrs'; exports.type = 'visitor'; exports.active = false; exports.description = 'removes specified attributes'; const DEFAULT_SEPARATOR = ':'; const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter. It should have a pattern to remove, otherwise the plugin is a noop. Config example: plugins: [ { name: "removeAttrs", params: { attrs: "(fill|stroke)" } } ] `; /** * Remove attributes * * @example elemSeparator * format: string * * @example preserveCurrentColor * format: boolean * * @example attrs: * * format: [ element* : attribute* : value* ] * * element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used) * attribute : regexp (wrapped into ^...$) * value : regexp (wrapped into ^...$), single * or omitted > all values * * examples: * * > basic: remove fill attribute * --- * removeAttrs: * attrs: 'fill' * * > remove fill attribute on path element * --- * attrs: 'path:fill' * * > remove fill attribute on path element where value is none * --- * attrs: 'path:fill:none' * * * > remove all fill and stroke attribute * --- * attrs: * - 'fill' * - 'stroke' * * [is same as] * * attrs: '(fill|stroke)' * * [is same as] * * attrs: '*:(fill|stroke)' * * [is same as] * * attrs: '.*:(fill|stroke)' * * [is same as] * * attrs: '.*:(fill|stroke):.*' * * * > remove all stroke related attributes * ---- * attrs: 'stroke.*' * * * @author Benny Schudel * * @type {import('../lib/types').Plugin<{ * elemSeparator?: string, * preserveCurrentColor?: boolean, * attrs: string | Array * }>} */ exports.fn = (root, params) => { if (typeof params.attrs == 'undefined') { console.warn(ENOATTRS); return null; } const elemSeparator = typeof params.elemSeparator == 'string' ? params.elemSeparator : DEFAULT_SEPARATOR; const preserveCurrentColor = typeof params.preserveCurrentColor == 'boolean' ? params.preserveCurrentColor : false; const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs]; return { element: { enter: (node) => { for (let pattern of attrs) { // if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value* if (pattern.includes(elemSeparator) === false) { pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join( '' ); // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value } else if (pattern.split(elemSeparator).length < 3) { pattern = [pattern, elemSeparator, '.*'].join(''); } // create regexps for element, attribute name, and attribute value const list = pattern.split(elemSeparator).map((value) => { // adjust single * to match anything if (value === '*') { value = '.*'; } return new RegExp(['^', value, '$'].join(''), 'i'); }); // matches element if (list[0].test(node.name)) { // loop attributes for (const [name, value] of Object.entries(node.attributes)) { const isFillCurrentColor = preserveCurrentColor && name == 'fill' && value == 'currentColor'; const isStrokeCurrentColor = preserveCurrentColor && name == 'stroke' && value == 'currentColor'; if ( !isFillCurrentColor && !isStrokeCurrentColor && // matches attribute name list[1].test(name) && // matches attribute value list[2].test(value) ) { delete node.attributes[name]; } } } } }, }, }; };