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. /**
  34. * Add hack to selected names
  35. */
  36. static hack(klass) {
  37. if (!this.hacks) {
  38. this.hacks = {}
  39. }
  40. return klass.names.map(name => {
  41. this.hacks[name] = klass
  42. return this.hacks[name]
  43. })
  44. }
  45. /**
  46. * Load hacks for some names
  47. */
  48. static load(name, prefixes, all) {
  49. let Klass = this.hacks && this.hacks[name]
  50. if (Klass) {
  51. return new Klass(name, prefixes, all)
  52. } else {
  53. return new this(name, prefixes, all)
  54. }
  55. }
  56. /**
  57. * Clone node and clean autprefixer custom caches
  58. */
  59. static clone(node, overrides) {
  60. let cloned = clone(node)
  61. for (let name in overrides) {
  62. cloned[name] = overrides[name]
  63. }
  64. return cloned
  65. }
  66. constructor(name, prefixes, all) {
  67. this.prefixes = prefixes
  68. this.name = name
  69. this.all = all
  70. }
  71. /**
  72. * Find prefix in node parents
  73. */
  74. parentPrefix(node) {
  75. let prefix
  76. if (typeof node._autoprefixerPrefix !== 'undefined') {
  77. prefix = node._autoprefixerPrefix
  78. } else if (node.type === 'decl' && node.prop[0] === '-') {
  79. prefix = vendor.prefix(node.prop)
  80. } else if (node.type === 'root') {
  81. prefix = false
  82. } else if (
  83. node.type === 'rule' &&
  84. node.selector.includes(':-') &&
  85. /:(-\w+-)/.test(node.selector)
  86. ) {
  87. prefix = node.selector.match(/:(-\w+-)/)[1]
  88. } else if (node.type === 'atrule' && node.name[0] === '-') {
  89. prefix = vendor.prefix(node.name)
  90. } else {
  91. prefix = this.parentPrefix(node.parent)
  92. }
  93. if (!Browsers.prefixes().includes(prefix)) {
  94. prefix = false
  95. }
  96. node._autoprefixerPrefix = prefix
  97. return node._autoprefixerPrefix
  98. }
  99. /**
  100. * Clone node with prefixes
  101. */
  102. process(node, result) {
  103. if (!this.check(node)) {
  104. return undefined
  105. }
  106. let parent = this.parentPrefix(node)
  107. let prefixes = this.prefixes.filter(
  108. prefix => !parent || parent === utils.removeNote(prefix)
  109. )
  110. let added = []
  111. for (let prefix of prefixes) {
  112. if (this.add(node, prefix, added.concat([prefix]), result)) {
  113. added.push(prefix)
  114. }
  115. }
  116. return added
  117. }
  118. /**
  119. * Shortcut for Prefixer.clone
  120. */
  121. clone(node, overrides) {
  122. return Prefixer.clone(node, overrides)
  123. }
  124. }
  125. module.exports = Prefixer