You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
3.1 KiB
150 lines
3.1 KiB
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
|
|
|