source: node_modules/autoprefixer/lib/supports.js@ 2058e5c

Last change on this file since 2058e5c was 2058e5c, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Working / before login

  • Property mode set to 100644
File size: 6.2 KB
Line 
1let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js')
2let feature = require('caniuse-lite/dist/unpacker/feature')
3let { parse } = require('postcss')
4
5let brackets = require('./brackets')
6let Browsers = require('./browsers')
7let utils = require('./utils')
8let Value = require('./value')
9
10let data = feature(featureQueries)
11
12let supported = []
13for (let browser in data.stats) {
14 let versions = data.stats[browser]
15 for (let version in versions) {
16 let support = versions[version]
17 if (/y/.test(support)) {
18 supported.push(browser + ' ' + version)
19 }
20 }
21}
22
23class Supports {
24 constructor(Prefixes, all) {
25 this.Prefixes = Prefixes
26 this.all = all
27 }
28
29 /**
30 * Add prefixes
31 */
32 add(nodes, all) {
33 return nodes.map(i => {
34 if (this.isProp(i)) {
35 let prefixed = this.prefixed(i[0])
36 if (prefixed.length > 1) {
37 return this.convert(prefixed)
38 }
39
40 return i
41 }
42
43 if (typeof i === 'object') {
44 return this.add(i, all)
45 }
46
47 return i
48 })
49 }
50
51 /**
52 * Clean brackets with one child
53 */
54 cleanBrackets(nodes) {
55 return nodes.map(i => {
56 if (typeof i !== 'object') {
57 return i
58 }
59
60 if (i.length === 1 && typeof i[0] === 'object') {
61 return this.cleanBrackets(i[0])
62 }
63
64 return this.cleanBrackets(i)
65 })
66 }
67
68 /**
69 * Add " or " between properties and convert it to brackets format
70 */
71 convert(progress) {
72 let result = ['']
73 for (let i of progress) {
74 result.push([`${i.prop}: ${i.value}`])
75 result.push(' or ')
76 }
77 result[result.length - 1] = ''
78 return result
79 }
80
81 /**
82 * Check global options
83 */
84 disabled(node) {
85 if (!this.all.options.grid) {
86 if (node.prop === 'display' && node.value.includes('grid')) {
87 return true
88 }
89 if (node.prop.includes('grid') || node.prop === 'justify-items') {
90 return true
91 }
92 }
93
94 if (this.all.options.flexbox === false) {
95 if (node.prop === 'display' && node.value.includes('flex')) {
96 return true
97 }
98 let other = ['order', 'justify-content', 'align-items', 'align-content']
99 if (node.prop.includes('flex') || other.includes(node.prop)) {
100 return true
101 }
102 }
103
104 return false
105 }
106
107 /**
108 * Return true if prefixed property has no unprefixed
109 */
110 isHack(all, unprefixed) {
111 let check = new RegExp(`(\\(|\\s)${utils.escapeRegexp(unprefixed)}:`)
112 return !check.test(all)
113 }
114
115 /**
116 * Return true if brackets node is "not" word
117 */
118 isNot(node) {
119 return typeof node === 'string' && /not\s*/i.test(node)
120 }
121
122 /**
123 * Return true if brackets node is "or" word
124 */
125 isOr(node) {
126 return typeof node === 'string' && /\s*or\s*/i.test(node)
127 }
128
129 /**
130 * Return true if brackets node is (prop: value)
131 */
132 isProp(node) {
133 return (
134 typeof node === 'object' &&
135 node.length === 1 &&
136 typeof node[0] === 'string'
137 )
138 }
139
140 /**
141 * Compress value functions into a string nodes
142 */
143 normalize(nodes) {
144 if (typeof nodes !== 'object') {
145 return nodes
146 }
147
148 nodes = nodes.filter(i => i !== '')
149
150 if (typeof nodes[0] === 'string') {
151 let firstNode = nodes[0].trim()
152
153 if (
154 firstNode.includes(':') ||
155 firstNode === 'selector' ||
156 firstNode === 'not selector'
157 ) {
158 return [brackets.stringify(nodes)]
159 }
160 }
161 return nodes.map(i => this.normalize(i))
162 }
163
164 /**
165 * Parse string into declaration property and value
166 */
167 parse(str) {
168 let parts = str.split(':')
169 let prop = parts[0]
170 let value = parts[1]
171 if (!value) value = ''
172 return [prop.trim(), value.trim()]
173 }
174
175 /**
176 * Return array of Declaration with all necessary prefixes
177 */
178 prefixed(str) {
179 let rule = this.virtual(str)
180 if (this.disabled(rule.first)) {
181 return rule.nodes
182 }
183
184 let result = { warn: () => null }
185
186 let prefixer = this.prefixer().add[rule.first.prop]
187 prefixer && prefixer.process && prefixer.process(rule.first, result)
188
189 for (let decl of rule.nodes) {
190 for (let value of this.prefixer().values('add', rule.first.prop)) {
191 value.process(decl)
192 }
193 Value.save(this.all, decl)
194 }
195
196 return rule.nodes
197 }
198
199 /**
200 * Return prefixer only with @supports supported browsers
201 */
202 prefixer() {
203 if (this.prefixerCache) {
204 return this.prefixerCache
205 }
206
207 let filtered = this.all.browsers.selected.filter(i => {
208 return supported.includes(i)
209 })
210
211 let browsers = new Browsers(
212 this.all.browsers.data,
213 filtered,
214 this.all.options
215 )
216 this.prefixerCache = new this.Prefixes(
217 this.all.data,
218 browsers,
219 this.all.options
220 )
221 return this.prefixerCache
222 }
223
224 /**
225 * Add prefixed declaration
226 */
227 process(rule) {
228 let ast = brackets.parse(rule.params)
229 ast = this.normalize(ast)
230 ast = this.remove(ast, rule.params)
231 ast = this.add(ast, rule.params)
232 ast = this.cleanBrackets(ast)
233 rule.params = brackets.stringify(ast)
234 }
235
236 /**
237 * Remove all unnecessary prefixes
238 */
239 remove(nodes, all) {
240 let i = 0
241 while (i < nodes.length) {
242 if (
243 !this.isNot(nodes[i - 1]) &&
244 this.isProp(nodes[i]) &&
245 this.isOr(nodes[i + 1])
246 ) {
247 if (this.toRemove(nodes[i][0], all)) {
248 nodes.splice(i, 2)
249 continue
250 }
251
252 i += 2
253 continue
254 }
255
256 if (typeof nodes[i] === 'object') {
257 nodes[i] = this.remove(nodes[i], all)
258 }
259
260 i += 1
261 }
262 return nodes
263 }
264
265 /**
266 * Return true if we need to remove node
267 */
268 toRemove(str, all) {
269 let [prop, value] = this.parse(str)
270 let unprefixed = this.all.unprefixed(prop)
271
272 let cleaner = this.all.cleaner()
273
274 if (
275 cleaner.remove[prop] &&
276 cleaner.remove[prop].remove &&
277 !this.isHack(all, unprefixed)
278 ) {
279 return true
280 }
281
282 for (let checker of cleaner.values('remove', unprefixed)) {
283 if (checker.check(value)) {
284 return true
285 }
286 }
287
288 return false
289 }
290
291 /**
292 * Create virtual rule to process it by prefixer
293 */
294 virtual(str) {
295 let [prop, value] = this.parse(str)
296 let rule = parse('a{}').first
297 rule.append({ prop, raws: { before: '' }, value })
298 return rule
299 }
300}
301
302module.exports = Supports
Note: See TracBrowser for help on using the repository browser.