UNPKG

3.22 kBJavaScriptView Raw
1let { list } = require('postcss')
2
3let OldSelector = require('./old-selector')
4let Prefixer = require('./prefixer')
5let Browsers = require('./browsers')
6let utils = require('./utils')
7
8class Selector extends Prefixer {
9 constructor(name, prefixes, all) {
10 super(name, prefixes, all)
11 this.regexpCache = new Map()
12 }
13
14 /**
15 * Is rule selectors need to be prefixed
16 */
17 check(rule) {
18 if (rule.selector.includes(this.name)) {
19 return !!rule.selector.match(this.regexp())
20 }
21
22 return false
23 }
24
25 /**
26 * Return prefixed version of selector
27 */
28 prefixed(prefix) {
29 return this.name.replace(/^(\W*)/, `$1${prefix}`)
30 }
31
32 /**
33 * Lazy loadRegExp for name
34 */
35 regexp(prefix) {
36 if (!this.regexpCache.has(prefix)) {
37 let name = prefix ? this.prefixed(prefix) : this.name
38 this.regexpCache.set(
39 prefix,
40 new RegExp(`(^|[^:"'=])${utils.escapeRegexp(name)}`, 'gi')
41 )
42 }
43
44 return this.regexpCache.get(prefix)
45 }
46
47 /**
48 * All possible prefixes
49 */
50 possible() {
51 return Browsers.prefixes()
52 }
53
54 /**
55 * Return all possible selector prefixes
56 */
57 prefixeds(rule) {
58 if (rule._autoprefixerPrefixeds) {
59 if (rule._autoprefixerPrefixeds[this.name]) {
60 return rule._autoprefixerPrefixeds
61 }
62 } else {
63 rule._autoprefixerPrefixeds = {}
64 }
65
66 let prefixeds = {}
67 if (rule.selector.includes(',')) {
68 let ruleParts = list.comma(rule.selector)
69 let toProcess = ruleParts.filter(el => el.includes(this.name))
70
71 for (let prefix of this.possible()) {
72 prefixeds[prefix] = toProcess
73 .map(el => this.replace(el, prefix))
74 .join(', ')
75 }
76 } else {
77 for (let prefix of this.possible()) {
78 prefixeds[prefix] = this.replace(rule.selector, prefix)
79 }
80 }
81
82 rule._autoprefixerPrefixeds[this.name] = prefixeds
83 return rule._autoprefixerPrefixeds
84 }
85
86 /**
87 * Is rule already prefixed before
88 */
89 already(rule, prefixeds, prefix) {
90 let index = rule.parent.index(rule) - 1
91
92 while (index >= 0) {
93 let before = rule.parent.nodes[index]
94
95 if (before.type !== 'rule') {
96 return false
97 }
98
99 let some = false
100 for (let key in prefixeds[this.name]) {
101 let prefixed = prefixeds[this.name][key]
102 if (before.selector === prefixed) {
103 if (prefix === key) {
104 return true
105 } else {
106 some = true
107 break
108 }
109 }
110 }
111 if (!some) {
112 return false
113 }
114
115 index -= 1
116 }
117
118 return false
119 }
120
121 /**
122 * Replace selectors by prefixed one
123 */
124 replace(selector, prefix) {
125 return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)
126 }
127
128 /**
129 * Clone and add prefixes for at-rule
130 */
131 add(rule, prefix) {
132 let prefixeds = this.prefixeds(rule)
133
134 if (this.already(rule, prefixeds, prefix)) {
135 return
136 }
137
138 let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] })
139 rule.parent.insertBefore(rule, cloned)
140 }
141
142 /**
143 * Return function to fast find prefixed selector
144 */
145 old(prefix) {
146 return new OldSelector(this, prefix)
147 }
148}
149
150module.exports = Selector