declaration.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. let Prefixer = require('./prefixer')
  2. let Browsers = require('./browsers')
  3. let utils = require('./utils')
  4. class Declaration extends Prefixer {
  5. /**
  6. * Always true, because we already get prefixer by property name
  7. */
  8. check(/* decl */) {
  9. return true
  10. }
  11. /**
  12. * Return prefixed version of property
  13. */
  14. prefixed(prop, prefix) {
  15. return prefix + prop
  16. }
  17. /**
  18. * Return unprefixed version of property
  19. */
  20. normalize(prop) {
  21. return prop
  22. }
  23. /**
  24. * Check `value`, that it contain other prefixes, rather than `prefix`
  25. */
  26. otherPrefixes(value, prefix) {
  27. for (let other of Browsers.prefixes()) {
  28. if (other === prefix) {
  29. continue
  30. }
  31. if (value.includes(other)) {
  32. return value.replace(/var\([^)]+\)/, '').includes(other)
  33. }
  34. }
  35. return false
  36. }
  37. /**
  38. * Set prefix to declaration
  39. */
  40. set(decl, prefix) {
  41. decl.prop = this.prefixed(decl.prop, prefix)
  42. return decl
  43. }
  44. /**
  45. * Should we use visual cascade for prefixes
  46. */
  47. needCascade(decl) {
  48. if (!decl._autoprefixerCascade) {
  49. decl._autoprefixerCascade =
  50. this.all.options.cascade !== false && decl.raw('before').includes('\n')
  51. }
  52. return decl._autoprefixerCascade
  53. }
  54. /**
  55. * Return maximum length of possible prefixed property
  56. */
  57. maxPrefixed(prefixes, decl) {
  58. if (decl._autoprefixerMax) {
  59. return decl._autoprefixerMax
  60. }
  61. let max = 0
  62. for (let prefix of prefixes) {
  63. prefix = utils.removeNote(prefix)
  64. if (prefix.length > max) {
  65. max = prefix.length
  66. }
  67. }
  68. decl._autoprefixerMax = max
  69. return decl._autoprefixerMax
  70. }
  71. /**
  72. * Calculate indentation to create visual cascade
  73. */
  74. calcBefore(prefixes, decl, prefix = '') {
  75. let max = this.maxPrefixed(prefixes, decl)
  76. let diff = max - utils.removeNote(prefix).length
  77. let before = decl.raw('before')
  78. if (diff > 0) {
  79. before += Array(diff).fill(' ').join('')
  80. }
  81. return before
  82. }
  83. /**
  84. * Remove visual cascade
  85. */
  86. restoreBefore(decl) {
  87. let lines = decl.raw('before').split('\n')
  88. let min = lines[lines.length - 1]
  89. this.all.group(decl).up(prefixed => {
  90. let array = prefixed.raw('before').split('\n')
  91. let last = array[array.length - 1]
  92. if (last.length < min.length) {
  93. min = last
  94. }
  95. })
  96. lines[lines.length - 1] = min
  97. decl.raws.before = lines.join('\n')
  98. }
  99. /**
  100. * Clone and insert new declaration
  101. */
  102. insert(decl, prefix, prefixes) {
  103. let cloned = this.set(this.clone(decl), prefix)
  104. if (!cloned) return undefined
  105. let already = decl.parent.some(
  106. i => i.prop === cloned.prop && i.value === cloned.value
  107. )
  108. if (already) {
  109. return undefined
  110. }
  111. if (this.needCascade(decl)) {
  112. cloned.raws.before = this.calcBefore(prefixes, decl, prefix)
  113. }
  114. return decl.parent.insertBefore(decl, cloned)
  115. }
  116. /**
  117. * Did this declaration has this prefix above
  118. */
  119. isAlready(decl, prefixed) {
  120. let already = this.all.group(decl).up(i => i.prop === prefixed)
  121. if (!already) {
  122. already = this.all.group(decl).down(i => i.prop === prefixed)
  123. }
  124. return already
  125. }
  126. /**
  127. * Clone and add prefixes for declaration
  128. */
  129. add(decl, prefix, prefixes, result) {
  130. let prefixed = this.prefixed(decl.prop, prefix)
  131. if (
  132. this.isAlready(decl, prefixed) ||
  133. this.otherPrefixes(decl.value, prefix)
  134. ) {
  135. return undefined
  136. }
  137. return this.insert(decl, prefix, prefixes, result)
  138. }
  139. /**
  140. * Add spaces for visual cascade
  141. */
  142. process(decl, result) {
  143. if (!this.needCascade(decl)) {
  144. super.process(decl, result)
  145. return
  146. }
  147. let prefixes = super.process(decl, result)
  148. if (!prefixes || !prefixes.length) {
  149. return
  150. }
  151. this.restoreBefore(decl)
  152. decl.raws.before = this.calcBefore(prefixes, decl)
  153. }
  154. /**
  155. * Return list of prefixed properties to clean old prefixes
  156. */
  157. old(prop, prefix) {
  158. return [this.prefixed(prop, prefix)]
  159. }
  160. }
  161. module.exports = Declaration