summaryrefslogtreecommitdiff
path: root/node_modules/autoprefixer/lib/supports.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/autoprefixer/lib/supports.js
Docs
Diffstat (limited to 'node_modules/autoprefixer/lib/supports.js')
-rw-r--r--node_modules/autoprefixer/lib/supports.js302
1 files changed, 302 insertions, 0 deletions
diff --git a/node_modules/autoprefixer/lib/supports.js b/node_modules/autoprefixer/lib/supports.js
new file mode 100644
index 0000000..58bd5af
--- /dev/null
+++ b/node_modules/autoprefixer/lib/supports.js
@@ -0,0 +1,302 @@
+let featureQueries = require('caniuse-lite/data/features/css-featurequeries.js')
+let feature = require('caniuse-lite/dist/unpacker/feature')
+let { parse } = require('postcss')
+
+let Browsers = require('./browsers')
+let brackets = require('./brackets')
+let Value = require('./value')
+let utils = require('./utils')
+
+let data = feature(featureQueries)
+
+let supported = []
+for (let browser in data.stats) {
+ let versions = data.stats[browser]
+ for (let version in versions) {
+ let support = versions[version]
+ if (/y/.test(support)) {
+ supported.push(browser + ' ' + version)
+ }
+ }
+}
+
+class Supports {
+ constructor(Prefixes, all) {
+ this.Prefixes = Prefixes
+ this.all = all
+ }
+
+ /**
+ * Add prefixes
+ */
+ add(nodes, all) {
+ return nodes.map(i => {
+ if (this.isProp(i)) {
+ let prefixed = this.prefixed(i[0])
+ if (prefixed.length > 1) {
+ return this.convert(prefixed)
+ }
+
+ return i
+ }
+
+ if (typeof i === 'object') {
+ return this.add(i, all)
+ }
+
+ return i
+ })
+ }
+
+ /**
+ * Clean brackets with one child
+ */
+ cleanBrackets(nodes) {
+ return nodes.map(i => {
+ if (typeof i !== 'object') {
+ return i
+ }
+
+ if (i.length === 1 && typeof i[0] === 'object') {
+ return this.cleanBrackets(i[0])
+ }
+
+ return this.cleanBrackets(i)
+ })
+ }
+
+ /**
+ * Add " or " between properties and convert it to brackets format
+ */
+ convert(progress) {
+ let result = ['']
+ for (let i of progress) {
+ result.push([`${i.prop}: ${i.value}`])
+ result.push(' or ')
+ }
+ result[result.length - 1] = ''
+ return result
+ }
+
+ /**
+ * Check global options
+ */
+ disabled(node) {
+ if (!this.all.options.grid) {
+ if (node.prop === 'display' && node.value.includes('grid')) {
+ return true
+ }
+ if (node.prop.includes('grid') || node.prop === 'justify-items') {
+ return true
+ }
+ }
+
+ if (this.all.options.flexbox === false) {
+ if (node.prop === 'display' && node.value.includes('flex')) {
+ return true
+ }
+ let other = ['order', 'justify-content', 'align-items', 'align-content']
+ if (node.prop.includes('flex') || other.includes(node.prop)) {
+ return true
+ }
+ }
+
+ return false
+ }
+
+ /**
+ * Return true if prefixed property has no unprefixed
+ */
+ isHack(all, unprefixed) {
+ let check = new RegExp(`(\\(|\\s)${utils.escapeRegexp(unprefixed)}:`)
+ return !check.test(all)
+ }
+
+ /**
+ * Return true if brackets node is "not" word
+ */
+ isNot(node) {
+ return typeof node === 'string' && /not\s*/i.test(node)
+ }
+
+ /**
+ * Return true if brackets node is "or" word
+ */
+ isOr(node) {
+ return typeof node === 'string' && /\s*or\s*/i.test(node)
+ }
+
+ /**
+ * Return true if brackets node is (prop: value)
+ */
+ isProp(node) {
+ return (
+ typeof node === 'object' &&
+ node.length === 1 &&
+ typeof node[0] === 'string'
+ )
+ }
+
+ /**
+ * Compress value functions into a string nodes
+ */
+ normalize(nodes) {
+ if (typeof nodes !== 'object') {
+ return nodes
+ }
+
+ nodes = nodes.filter(i => i !== '')
+
+ if (typeof nodes[0] === 'string') {
+ let firstNode = nodes[0].trim()
+
+ if (
+ firstNode.includes(':') ||
+ firstNode === 'selector' ||
+ firstNode === 'not selector'
+ ) {
+ return [brackets.stringify(nodes)]
+ }
+ }
+ return nodes.map(i => this.normalize(i))
+ }
+
+ /**
+ * Parse string into declaration property and value
+ */
+ parse(str) {
+ let parts = str.split(':')
+ let prop = parts[0]
+ let value = parts[1]
+ if (!value) value = ''
+ return [prop.trim(), value.trim()]
+ }
+
+ /**
+ * Return array of Declaration with all necessary prefixes
+ */
+ prefixed(str) {
+ let rule = this.virtual(str)
+ if (this.disabled(rule.first)) {
+ return rule.nodes
+ }
+
+ let result = { warn: () => null }
+
+ let prefixer = this.prefixer().add[rule.first.prop]
+ prefixer && prefixer.process && prefixer.process(rule.first, result)
+
+ for (let decl of rule.nodes) {
+ for (let value of this.prefixer().values('add', rule.first.prop)) {
+ value.process(decl)
+ }
+ Value.save(this.all, decl)
+ }
+
+ return rule.nodes
+ }
+
+ /**
+ * Return prefixer only with @supports supported browsers
+ */
+ prefixer() {
+ if (this.prefixerCache) {
+ return this.prefixerCache
+ }
+
+ let filtered = this.all.browsers.selected.filter(i => {
+ return supported.includes(i)
+ })
+
+ let browsers = new Browsers(
+ this.all.browsers.data,
+ filtered,
+ this.all.options
+ )
+ this.prefixerCache = new this.Prefixes(
+ this.all.data,
+ browsers,
+ this.all.options
+ )
+ return this.prefixerCache
+ }
+
+ /**
+ * Add prefixed declaration
+ */
+ process(rule) {
+ let ast = brackets.parse(rule.params)
+ ast = this.normalize(ast)
+ ast = this.remove(ast, rule.params)
+ ast = this.add(ast, rule.params)
+ ast = this.cleanBrackets(ast)
+ rule.params = brackets.stringify(ast)
+ }
+
+ /**
+ * Remove all unnecessary prefixes
+ */
+ remove(nodes, all) {
+ let i = 0
+ while (i < nodes.length) {
+ if (
+ !this.isNot(nodes[i - 1]) &&
+ this.isProp(nodes[i]) &&
+ this.isOr(nodes[i + 1])
+ ) {
+ if (this.toRemove(nodes[i][0], all)) {
+ nodes.splice(i, 2)
+ continue
+ }
+
+ i += 2
+ continue
+ }
+
+ if (typeof nodes[i] === 'object') {
+ nodes[i] = this.remove(nodes[i], all)
+ }
+
+ i += 1
+ }
+ return nodes
+ }
+
+ /**
+ * Return true if we need to remove node
+ */
+ toRemove(str, all) {
+ let [prop, value] = this.parse(str)
+ let unprefixed = this.all.unprefixed(prop)
+
+ let cleaner = this.all.cleaner()
+
+ if (
+ cleaner.remove[prop] &&
+ cleaner.remove[prop].remove &&
+ !this.isHack(all, unprefixed)
+ ) {
+ return true
+ }
+
+ for (let checker of cleaner.values('remove', unprefixed)) {
+ if (checker.check(value)) {
+ return true
+ }
+ }
+
+ return false
+ }
+
+ /**
+ * Create virtual rule to process it by prefixer
+ */
+ virtual(str) {
+ let [prop, value] = this.parse(str)
+ let rule = parse('a{}').first
+ rule.append({ prop, raws: { before: '' }, value })
+ return rule
+ }
+}
+
+module.exports = Supports