index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. let valueParser = require('postcss-value-parser')
  2. function parseValue (value) {
  3. let parsed = value.match(/([\d.-]+)(.*)/)
  4. if (!parsed || !parsed[1] || !parsed[2] || isNaN(parsed[1])) {
  5. return undefined
  6. }
  7. return [parseFloat(parsed[1]), parsed[2]]
  8. }
  9. function compose (first, second, third) {
  10. if (first && second && third) {
  11. return `max(${first}, min(${second}, ${third}))`
  12. }
  13. if (first && second) {
  14. return `max(${first}, ${second})`
  15. }
  16. return first
  17. }
  18. function updateValue (declaration, value, preserve) {
  19. let newValue = value
  20. let newValueAst = valueParser(value)
  21. let valueAST = valueParser(declaration.value)
  22. // Walk can't be interrupted, so we only care about first
  23. let foundClamp = false
  24. valueAST.walk((node, index, nodes) => {
  25. let isClamp = node.type === 'function' && node.value === 'clamp'
  26. if (!isClamp || foundClamp) {
  27. return
  28. }
  29. foundClamp = true
  30. nodes[index] = newValueAst
  31. })
  32. if (foundClamp) {
  33. newValue = valueAST.toString()
  34. }
  35. if (preserve) {
  36. declaration.cloneBefore({ value: newValue })
  37. } else {
  38. declaration.value = newValue
  39. }
  40. }
  41. module.exports = opts => {
  42. opts = opts || {}
  43. let precalculate = opts.precalculate ? Boolean(opts.precalculate) : false
  44. let preserve = opts.preserve ? Boolean(opts.preserve) : false
  45. return {
  46. postcssPlugin: 'postcss-clamp',
  47. Declaration (decl) {
  48. if (!decl || !decl.value.includes('clamp')) {
  49. return
  50. }
  51. valueParser(decl.value).walk(node => {
  52. let nodes = node.nodes
  53. if (
  54. node.type !== 'function' ||
  55. node.value !== 'clamp' ||
  56. nodes.length !== 5
  57. ) {
  58. return
  59. }
  60. let first = nodes[0]
  61. let second = nodes[2]
  62. let third = nodes[4]
  63. let naive = compose(
  64. valueParser.stringify(first),
  65. valueParser.stringify(second),
  66. valueParser.stringify(third)
  67. )
  68. if (!precalculate || second.type !== 'word' || third.type !== 'word') {
  69. updateValue(decl, naive, preserve)
  70. return
  71. }
  72. let parsedSecond = parseValue(second.value)
  73. let parsedThird = parseValue(third.value)
  74. if (parsedSecond === undefined || parsedThird === undefined) {
  75. updateValue(decl, naive, preserve)
  76. return
  77. }
  78. let [secondValue, secondUnit] = parsedSecond
  79. let [thirdValue, thirdUnit] = parsedThird
  80. if (secondUnit !== thirdUnit) {
  81. updateValue(decl, naive, preserve)
  82. return
  83. }
  84. let parsedFirst = parseValue(first.value)
  85. if (parsedFirst === undefined) {
  86. let secondThirdValue = `${secondValue + thirdValue}${secondUnit}`
  87. updateValue(
  88. decl,
  89. compose(valueParser.stringify(first), secondThirdValue),
  90. preserve
  91. )
  92. return
  93. }
  94. let [firstValue, firstUnit] = parsedFirst
  95. if (firstUnit !== secondUnit) {
  96. let secondThirdValue = `${secondValue + thirdValue}${secondUnit}`
  97. updateValue(
  98. decl,
  99. compose(valueParser.stringify(first), secondThirdValue),
  100. preserve
  101. )
  102. return
  103. }
  104. updateValue(
  105. decl,
  106. compose(`${firstValue + secondValue + thirdValue}${secondUnit}`),
  107. preserve
  108. )
  109. })
  110. }
  111. }
  112. }
  113. module.exports.postcss = true