let valueParser = require('postcss-value-parser') function parseValue (value) { let parsed = value.match(/([\d.-]+)(.*)/) if (!parsed || !parsed[1] || !parsed[2] || isNaN(parsed[1])) { return undefined } return [parseFloat(parsed[1]), parsed[2]] } function compose (first, second, third) { if (first && second && third) { return `max(${first}, min(${second}, ${third}))` } if (first && second) { return `max(${first}, ${second})` } return first } function updateValue (declaration, value, preserve) { let newValue = value let newValueAst = valueParser(value) let valueAST = valueParser(declaration.value) // Walk can't be interrupted, so we only care about first let foundClamp = false valueAST.walk((node, index, nodes) => { let isClamp = node.type === 'function' && node.value === 'clamp' if (!isClamp || foundClamp) { return } foundClamp = true nodes[index] = newValueAst }) if (foundClamp) { newValue = valueAST.toString() } if (preserve) { declaration.cloneBefore({ value: newValue }) } else { declaration.value = newValue } } module.exports = opts => { opts = opts || {} let precalculate = opts.precalculate ? Boolean(opts.precalculate) : false let preserve = opts.preserve ? Boolean(opts.preserve) : false return { postcssPlugin: 'postcss-clamp', Declaration (decl) { if (!decl || !decl.value.includes('clamp')) { return } valueParser(decl.value).walk(node => { let nodes = node.nodes if ( node.type !== 'function' || node.value !== 'clamp' || nodes.length !== 5 ) { return } let first = nodes[0] let second = nodes[2] let third = nodes[4] let naive = compose( valueParser.stringify(first), valueParser.stringify(second), valueParser.stringify(third) ) if (!precalculate || second.type !== 'word' || third.type !== 'word') { updateValue(decl, naive, preserve) return } let parsedSecond = parseValue(second.value) let parsedThird = parseValue(third.value) if (parsedSecond === undefined || parsedThird === undefined) { updateValue(decl, naive, preserve) return } let [secondValue, secondUnit] = parsedSecond let [thirdValue, thirdUnit] = parsedThird if (secondUnit !== thirdUnit) { updateValue(decl, naive, preserve) return } let parsedFirst = parseValue(first.value) if (parsedFirst === undefined) { let secondThirdValue = `${secondValue + thirdValue}${secondUnit}` updateValue( decl, compose(valueParser.stringify(first), secondThirdValue), preserve ) return } let [firstValue, firstUnit] = parsedFirst if (firstUnit !== secondUnit) { let secondThirdValue = `${secondValue + thirdValue}${secondUnit}` updateValue( decl, compose(valueParser.stringify(first), secondThirdValue), preserve ) return } updateValue( decl, compose(`${firstValue + secondValue + thirdValue}${secondUnit}`), preserve ) }) } } } module.exports.postcss = true