123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- let { list } = require('postcss')
- let OldSelector = require('./old-selector')
- let Prefixer = require('./prefixer')
- let Browsers = require('./browsers')
- let utils = require('./utils')
- class Selector extends Prefixer {
- constructor(name, prefixes, all) {
- super(name, prefixes, all)
- this.regexpCache = new Map()
- }
- /**
- * Is rule selectors need to be prefixed
- */
- check(rule) {
- if (rule.selector.includes(this.name)) {
- return !!rule.selector.match(this.regexp())
- }
- return false
- }
- /**
- * Return prefixed version of selector
- */
- prefixed(prefix) {
- return this.name.replace(/^(\W*)/, `$1${prefix}`)
- }
- /**
- * Lazy loadRegExp for name
- */
- regexp(prefix) {
- if (!this.regexpCache.has(prefix)) {
- let name = prefix ? this.prefixed(prefix) : this.name
- this.regexpCache.set(
- prefix,
- new RegExp(`(^|[^:"'=])${utils.escapeRegexp(name)}`, 'gi')
- )
- }
- return this.regexpCache.get(prefix)
- }
- /**
- * All possible prefixes
- */
- possible() {
- return Browsers.prefixes()
- }
- /**
- * Return all possible selector prefixes
- */
- prefixeds(rule) {
- if (rule._autoprefixerPrefixeds) {
- if (rule._autoprefixerPrefixeds[this.name]) {
- return rule._autoprefixerPrefixeds
- }
- } else {
- rule._autoprefixerPrefixeds = {}
- }
- let prefixeds = {}
- if (rule.selector.includes(',')) {
- let ruleParts = list.comma(rule.selector)
- let toProcess = ruleParts.filter(el => el.includes(this.name))
- for (let prefix of this.possible()) {
- prefixeds[prefix] = toProcess
- .map(el => this.replace(el, prefix))
- .join(', ')
- }
- } else {
- for (let prefix of this.possible()) {
- prefixeds[prefix] = this.replace(rule.selector, prefix)
- }
- }
- rule._autoprefixerPrefixeds[this.name] = prefixeds
- return rule._autoprefixerPrefixeds
- }
- /**
- * Is rule already prefixed before
- */
- already(rule, prefixeds, prefix) {
- let index = rule.parent.index(rule) - 1
- while (index >= 0) {
- let before = rule.parent.nodes[index]
- if (before.type !== 'rule') {
- return false
- }
- let some = false
- for (let key in prefixeds[this.name]) {
- let prefixed = prefixeds[this.name][key]
- if (before.selector === prefixed) {
- if (prefix === key) {
- return true
- } else {
- some = true
- break
- }
- }
- }
- if (!some) {
- return false
- }
- index -= 1
- }
- return false
- }
- /**
- * Replace selectors by prefixed one
- */
- replace(selector, prefix) {
- return selector.replace(this.regexp(), `$1${this.prefixed(prefix)}`)
- }
- /**
- * Clone and add prefixes for at-rule
- */
- add(rule, prefix) {
- let prefixeds = this.prefixeds(rule)
- if (this.already(rule, prefixeds, prefix)) {
- return
- }
- let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] })
- rule.parent.insertBefore(rule, cloned)
- }
- /**
- * Return function to fast find prefixed selector
- */
- old(prefix) {
- return new OldSelector(this, prefix)
- }
- }
- module.exports = Selector
|