basant307/AI_Governance_Project
048
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 = Prefixes26 this.all = all27 }28 29 /**30 * Add prefixes31 */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 i41 }42 43 if (typeof i === 'object') {44 return this.add(i, all)45 }46 47 return i48 })49 }50 51 /**52 * Clean brackets with one child53 */54 cleanBrackets(nodes) {55 return nodes.map(i => {56 if (typeof i !== 'object') {57 return i58 }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 format70 */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 result79 }80 81 /**82 * Check global options83 */84 disabled(node) {85 if (!this.all.options.grid) {86 if (node.prop === 'display' && node.value.includes('grid')) {87 return true88 }89 if (node.prop.includes('grid') || node.prop === 'justify-items') {90 return true91 }92 }93 94 if (this.all.options.flexbox === false) {95 if (node.prop === 'display' && node.value.includes('flex')) {96 return true97 }98 let other = ['order', 'justify-content', 'align-items', 'align-content']99 if (node.prop.includes('flex') || other.includes(node.prop)) {100 return true101 }102 }103 104 return false105 }106 107 /**108 * Return true if prefixed property has no unprefixed109 */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" word117 */118 isNot(node) {119 return typeof node === 'string' && /not\s*/i.test(node)120 }121 122 /**123 * Return true if brackets node is "or" word124 */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 nodes142 */143 normalize(nodes) {144 if (typeof nodes !== 'object') {145 return nodes146 }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 value166 */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 prefixes177 */178 prefixed(str) {179 let rule = this.virtual(str)180 if (this.disabled(rule.first)) {181 return rule.nodes182 }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.nodes197 }198 199 /**200 * Return prefixer only with @supports supported browsers201 */202 prefixer() {203 if (this.prefixerCache) {204 return this.prefixerCache205 }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.options215 )216 this.prefixerCache = new this.Prefixes(217 this.all.data,218 browsers,219 this.all.options220 )221 return this.prefixerCache222 }223 224 /**225 * Add prefixed declaration226 */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 prefixes238 */239 remove(nodes, all) {240 let i = 0241 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 continue250 }251 252 i += 2253 continue254 }255 256 if (typeof nodes[i] === 'object') {257 nodes[i] = this.remove(nodes[i], all)258 }259 260 i += 1261 }262 return nodes263 }264 265 /**266 * Return true if we need to remove node267 */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 true280 }281 282 for (let checker of cleaner.values('remove', unprefixed)) {283 if (checker.check(value)) {284 return true285 }286 }287 288 return false289 }290 291 /**292 * Create virtual rule to process it by prefixer293 */294 virtual(str) {295 let [prop, value] = this.parse(str)296 let rule = parse('a{}').first297 rule.append({ prop, raws: { before: '' }, value })298 return rule299 }300}301 302module.exports = Supports303 