prefixer.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. let Browsers = require('./browsers')
  2. let vendor = require('./vendor')
  3. let utils = require('./utils')
  4. /**
  5. * Recursively clone objects
  6. */
  7. function clone(obj, parent) {
  8. let cloned = new obj.constructor()
  9. for (let i of Object.keys(obj || {})) {
  10. let value = obj[i]
  11. if (i === 'parent' && typeof value === 'object') {
  12. if (parent) {
  13. cloned[i] = parent
  14. }
  15. } else if (i === 'source' || i === null) {
  16. cloned[i] = value
  17. } else if (Array.isArray(value)) {
  18. cloned[i] = value.map(x => clone(x, cloned))
  19. } else if (
  20. i !== '_autoprefixerPrefix' &&
  21. i !== '_autoprefixerValues' &&
  22. i !== 'proxyCache'
  23. ) {
  24. if (typeof value === 'object' && value !== null) {
  25. value = clone(value, cloned)
  26. }
  27. cloned[i] = value
  28. }
  29. }
  30. return cloned
  31. }
  32. class Prefixer {
  33. constructor(name, prefixes, all) {
  34. this.prefixes = prefixes
  35. this.name = name
  36. this.all = all
  37. }
  38. /**
  39. * Clone node and clean autprefixer custom caches
  40. */
  41. static clone(node, overrides) {
  42. let cloned = clone(node)
  43. for (let name in overrides) {
  44. cloned[name] = overrides[name]
  45. }
  46. return cloned
  47. }
  48. /**
  49. * Add hack to selected names
  50. */
  51. static hack(klass) {
  52. if (!this.hacks) {
  53. this.hacks = {}
  54. }
  55. return klass.names.map(name => {
  56. this.hacks[name] = klass
  57. return this.hacks[name]
  58. })
  59. }
  60. /**
  61. * Load hacks for some names
  62. */
  63. static load(name, prefixes, all) {
  64. let Klass = this.hacks && this.hacks[name]
  65. if (Klass) {
  66. return new Klass(name, prefixes, all)
  67. } else {
  68. return new this(name, prefixes, all)
  69. }
  70. }
  71. /**
  72. * Shortcut for Prefixer.clone
  73. */
  74. clone(node, overrides) {
  75. return Prefixer.clone(node, overrides)
  76. }
  77. /**
  78. * Find prefix in node parents
  79. */
  80. parentPrefix(node) {
  81. let prefix
  82. if (typeof node._autoprefixerPrefix !== 'undefined') {
  83. prefix = node._autoprefixerPrefix
  84. } else if (node.type === 'decl' && node.prop[0] === '-') {
  85. prefix = vendor.prefix(node.prop)
  86. } else if (node.type === 'root') {
  87. prefix = false
  88. } else if (
  89. node.type === 'rule' &&
  90. node.selector.includes(':-') &&
  91. /:(-\w+-)/.test(node.selector)
  92. ) {
  93. prefix = node.selector.match(/:(-\w+-)/)[1]
  94. } else if (node.type === 'atrule' && node.name[0] === '-') {
  95. prefix = vendor.prefix(node.name)
  96. } else {
  97. prefix = this.parentPrefix(node.parent)
  98. }
  99. if (!Browsers.prefixes().includes(prefix)) {
  100. prefix = false
  101. }
  102. node._autoprefixerPrefix = prefix
  103. return node._autoprefixerPrefix
  104. }
  105. /**
  106. * Clone node with prefixes
  107. */
  108. process(node, result) {
  109. if (!this.check(node)) {
  110. return undefined
  111. }
  112. let parent = this.parentPrefix(node)
  113. let prefixes = this.prefixes.filter(
  114. prefix => !parent || parent === utils.removeNote(prefix)
  115. )
  116. let added = []
  117. for (let prefix of prefixes) {
  118. if (this.add(node, prefix, added.concat([prefix]), result)) {
  119. added.push(prefix)
  120. }
  121. }
  122. return added
  123. }
  124. }
  125. module.exports = Prefixer