diff --git a/src/transformers/removeAttributes.js b/src/transformers/removeAttributes.js index 9e7024e3..b55122bc 100644 --- a/src/transformers/removeAttributes.js +++ b/src/transformers/removeAttributes.js @@ -1,18 +1,56 @@ const posthtml = require('posthtml') const {get, merge} = require('lodash') -const removeAttributes = require('posthtml-remove-attributes') const defaultConfig = require('../generators/posthtml/defaultConfig') module.exports = async (html, config = {}, direct = false) => { const attributes = direct ? (Array.isArray(config) ? [...config] : []) : get(config, 'removeAttributes', []) const posthtmlOptions = merge(defaultConfig, get(config, 'build.posthtml.options', {})) - attributes.push({name: 'style'}, {name: 'class'}) + attributes.push('style', 'class') - // Allow omitting `value` key when removing empty attributes - attributes.forEach(attr => { - attr.value = attr.value || '' - }) + html = await posthtml([ + removeAttributes(attributes, posthtmlOptions) + ]).process(html, posthtmlOptions).then(result => result.html) - return posthtml([removeAttributes(attributes)]).process(html, posthtmlOptions).then(result => result.html) + return html +} + +/** + * Remove empty attributes with PostHTML + * + * Condition 1: + * `boolean` is for attributes without ="" (respects `recognizeNoValueAttribute` in PostHTML) + * `''` if the attribute included ="", i.e. style="" + * + * Condition 2: attribute value is a string and matches the one on the node + * + * Condition 3: same as 2, but for regular expressions + */ +const removeAttributes = (attributes = {}, posthtmlOptions = {}) => tree => { + const process = node => { + const normalizedAttrs = attributes.map(attribute => { + return { + name: get(attribute, 'name', typeof attribute === 'string' ? attribute : false), + value: get(attribute, 'value', get(posthtmlOptions, 'recognizeNoValueAttributes', true)) + } + }) + + if (node.attrs) { + normalizedAttrs.forEach(attr => { + const targetAttrValue = get(node.attrs, attr.name) + + if ( + typeof targetAttrValue === 'boolean' || targetAttrValue === '' || + (typeof attr.value === 'string' && node.attrs[attr.name] === attr.value) || + (attr.value instanceof RegExp && attr.value.test(node.attrs[attr.name])) + ) { + node.attrs[attr.name] = false + } + }) + } + + return node + } + + return tree.walk(process) } diff --git a/test/test-transformers.js b/test/test-transformers.js index b2b649eb..0c70bcc2 100644 --- a/test/test-transformers.js +++ b/test/test-transformers.js @@ -163,9 +163,16 @@ test('remove unused CSS (disabled)', async t => { }) test('remove attributes', async t => { - const html = await Maizzle.removeAttributes(`
`, [{name: 'role', value: 'article'}]) + const html = await Maizzle.removeAttributes( + `
`, + [ + {name: 'role', value: 'article'}, + 'remove', + {name: 'delete-me', value: /^with/} + ] + ) - t.is(html, '
') + t.is(html, '
') }) test('extra attributes', async t => {