scope-id.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. const postcss = require('postcss')
  2. const selectorParser = require('postcss-selector-parser')
  3. module.exports = postcss.plugin('add-id', ({ id }) => root => {
  4. const keyframes = Object.create(null)
  5. root.each(function rewriteSelector (node) {
  6. if (!node.selector) {
  7. // handle media queries
  8. if (node.type === 'atrule') {
  9. if (node.name === 'media' || node.name === 'supports') {
  10. node.each(rewriteSelector)
  11. } else if (/-?keyframes$/.test(node.name)) {
  12. // register keyframes
  13. keyframes[node.params] = node.params = node.params + '-' + id
  14. }
  15. }
  16. return
  17. }
  18. node.selector = selectorParser(selectors => {
  19. selectors.each(selector => {
  20. let node = null
  21. selector.each(n => {
  22. // ">>>" combinator
  23. if (n.type === 'combinator' && n.value === '>>>') {
  24. n.value = ' '
  25. n.spaces.before = n.spaces.after = ''
  26. return false
  27. }
  28. // /deep/ alias for >>>, since >>> doesn't work in SASS
  29. if (n.type === 'tag' && n.value === '/deep/') {
  30. const prev = n.prev()
  31. if (prev && prev.type === 'combinator' && prev.value === ' ') {
  32. prev.remove()
  33. }
  34. n.remove()
  35. return false
  36. }
  37. if (n.type !== 'pseudo' && n.type !== 'combinator') {
  38. node = n
  39. }
  40. })
  41. selector.insertAfter(node, selectorParser.attribute({
  42. attribute: id
  43. }))
  44. })
  45. }).process(node.selector).result
  46. })
  47. // If keyframes are found in this <style>, find and rewrite animation names
  48. // in declarations.
  49. // Caveat: this only works for keyframes and animation rules in the same
  50. // <style> element.
  51. if (Object.keys(keyframes).length) {
  52. root.walkDecls(decl => {
  53. // individual animation-name declaration
  54. if (/-?animation-name$/.test(decl.prop)) {
  55. decl.value = decl.value.split(',')
  56. .map(v => keyframes[v.trim()] || v.trim())
  57. .join(',')
  58. }
  59. // shorthand
  60. if (/-?animation$/.test(decl.prop)) {
  61. decl.value = decl.value.split(',')
  62. .map(v => {
  63. const vals = v.trim().split(/\s+/)
  64. const i = vals.findIndex(val => keyframes[val])
  65. if (i !== -1) {
  66. vals.splice(i, 1, keyframes[vals[i]])
  67. return vals.join(' ')
  68. } else {
  69. return v
  70. }
  71. })
  72. .join(',')
  73. }
  74. })
  75. }
  76. })