Upstream postcss/autoprefixer state of the touched files before 42b5e28ba2
This commit is contained in:
1 parent
bb453f7a17
commit
e47232149a
6 files changed
+1083
-19
No files matched your search
@@ -0,0 +1,477 @@
|
||||
let parser = require('postcss-value-parser')
|
||||
|
||||
let OldValue = require('../old-value')
|
||||
let utils = require('../utils')
|
||||
let Value = require('../value')
|
||||
|
||||
const IS_DIRECTION = /top|left|right|bottom/gi
|
||||
|
||||
class Gradient extends Value {
|
||||
/**
|
||||
* Do not add non-webkit prefixes for list-style and object
|
||||
*/
|
||||
add(decl, prefix, prefixes, result) {
|
||||
let p = decl.prop
|
||||
if (p.includes('mask')) {
|
||||
if (prefix === '-webkit-' || prefix === '-webkit- old') {
|
||||
return super.add(decl, prefix, prefixes, result)
|
||||
}
|
||||
} else if (
|
||||
p === 'list-style' ||
|
||||
p === 'list-style-image' ||
|
||||
p === 'content'
|
||||
) {
|
||||
if (prefix === '-webkit-' || prefix === '-webkit- old') {
|
||||
return super.add(decl, prefix, prefixes, result)
|
||||
}
|
||||
} else {
|
||||
return super.add(decl, prefix, prefixes, result)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Get div token from exists parameters
|
||||
*/
|
||||
cloneDiv(params) {
|
||||
for (let i of params) {
|
||||
if (i.type === 'div' && i.value === ',') {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return { after: ' ', type: 'div', value: ',' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Change colors syntax to old webkit
|
||||
*/
|
||||
colorStops(params) {
|
||||
let result = []
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let pos
|
||||
let param = params[i]
|
||||
let item
|
||||
if (i === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (param.length === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
let color = parser.stringify(param[0])
|
||||
if (param[1] && param[1].type === 'word') {
|
||||
pos = param[1].value
|
||||
} else if (param[2] && param[2].type === 'word') {
|
||||
pos = param[2].value
|
||||
}
|
||||
|
||||
let stop
|
||||
if (i === 1 && (!pos || pos === '0%')) {
|
||||
stop = `from(${color})`
|
||||
} else if (i === params.length - 1 && (!pos || pos === '100%')) {
|
||||
stop = `to(${color})`
|
||||
} else if (pos) {
|
||||
stop = `color-stop(${pos}, ${color})`
|
||||
} else {
|
||||
stop = `color-stop(${color})`
|
||||
}
|
||||
|
||||
let div = param[param.length - 1]
|
||||
params[i] = [{ type: 'word', value: stop }]
|
||||
if (div && div.type === 'div' && div.value === ',') {
|
||||
item = params[i].push(div)
|
||||
}
|
||||
result.push(item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Change new direction to old
|
||||
*/
|
||||
convertDirection(params) {
|
||||
if (params.length > 0) {
|
||||
if (params[0].value === 'to') {
|
||||
this.fixDirection(params)
|
||||
} else if (params[0].value.includes('deg')) {
|
||||
this.fixAngle(params)
|
||||
} else if (this.isRadial(params)) {
|
||||
this.fixRadial(params)
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 90 degrees
|
||||
*/
|
||||
fixAngle(params) {
|
||||
let first = params[0].value
|
||||
first = parseFloat(first)
|
||||
first = Math.abs(450 - first) % 360
|
||||
first = this.roundFloat(first, 3)
|
||||
params[0].value = `${first}deg`
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace `to top left` to `bottom right`
|
||||
*/
|
||||
fixDirection(params) {
|
||||
params.splice(0, 2)
|
||||
|
||||
for (let param of params) {
|
||||
if (param.type === 'div') {
|
||||
break
|
||||
}
|
||||
if (param.type === 'word') {
|
||||
param.value = this.revertDirection(param.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix radial direction syntax
|
||||
*/
|
||||
fixRadial(params) {
|
||||
let first = []
|
||||
let second = []
|
||||
let a, b, c, i, next
|
||||
|
||||
for (i = 0; i < params.length - 2; i++) {
|
||||
a = params[i]
|
||||
b = params[i + 1]
|
||||
c = params[i + 2]
|
||||
if (a.type === 'space' && b.value === 'at' && c.type === 'space') {
|
||||
next = i + 3
|
||||
break
|
||||
} else {
|
||||
first.push(a)
|
||||
}
|
||||
}
|
||||
|
||||
let div
|
||||
for (i = next; i < params.length; i++) {
|
||||
if (params[i].type === 'div') {
|
||||
div = params[i]
|
||||
break
|
||||
} else {
|
||||
second.push(params[i])
|
||||
}
|
||||
}
|
||||
|
||||
params.splice(0, i, ...second, div, ...first)
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for at word
|
||||
*/
|
||||
isRadial(params) {
|
||||
let state = 'before'
|
||||
for (let param of params) {
|
||||
if (state === 'before' && param.type === 'space') {
|
||||
state = 'at'
|
||||
} else if (state === 'at' && param.value === 'at') {
|
||||
state = 'after'
|
||||
} else if (state === 'after' && param.type === 'space') {
|
||||
return true
|
||||
} else if (param.type === 'div') {
|
||||
break
|
||||
} else {
|
||||
state = 'before'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace old direction to new
|
||||
*/
|
||||
newDirection(params) {
|
||||
if (!params[0]) {
|
||||
return params
|
||||
}
|
||||
if (params[0].value === 'to') {
|
||||
return params
|
||||
}
|
||||
IS_DIRECTION.lastIndex = 0 // reset search index of global regexp
|
||||
if (!IS_DIRECTION.test(params[0].value)) {
|
||||
return params
|
||||
}
|
||||
|
||||
params.unshift(
|
||||
{
|
||||
type: 'word',
|
||||
value: 'to'
|
||||
},
|
||||
{
|
||||
type: 'space',
|
||||
value: ' '
|
||||
}
|
||||
)
|
||||
|
||||
for (let i = 2; i < params.length; i++) {
|
||||
if (params[i].type === 'div') {
|
||||
break
|
||||
}
|
||||
if (params[i].type === 'word') {
|
||||
params[i].value = this.revertDirection(params[i].value)
|
||||
}
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize angle
|
||||
*/
|
||||
normalize(nodes, gradientName) {
|
||||
if (!nodes[0]) return nodes
|
||||
|
||||
if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) {
|
||||
nodes[0].value = this.normalizeUnit(nodes[0].value, 400)
|
||||
} else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) {
|
||||
nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI)
|
||||
} else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) {
|
||||
nodes[0].value = this.normalizeUnit(nodes[0].value, 1)
|
||||
} else if (nodes[0].value.includes('deg')) {
|
||||
let num = parseFloat(nodes[0].value)
|
||||
num = ((num % 360) + 360) % 360
|
||||
nodes[0].value = `${num}deg`
|
||||
}
|
||||
|
||||
if (
|
||||
gradientName === 'linear-gradient' ||
|
||||
gradientName === 'repeating-linear-gradient'
|
||||
) {
|
||||
let direction = nodes[0].value
|
||||
|
||||
// Unitless zero for `<angle>` values are allowed in CSS gradients and transforms.
|
||||
// Spec: https://github.com/w3c/csswg-drafts/commit/602789171429b2231223ab1e5acf8f7f11652eb3
|
||||
if (direction === '0deg' || direction === '0') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'top')
|
||||
} else if (direction === '90deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'right')
|
||||
} else if (direction === '180deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom') // default value
|
||||
} else if (direction === '270deg') {
|
||||
nodes = this.replaceFirst(nodes, 'to', ' ', 'left')
|
||||
}
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert angle unit to deg
|
||||
*/
|
||||
normalizeUnit(str, full) {
|
||||
let num = parseFloat(str)
|
||||
let deg = (num / full) * 360
|
||||
return `${deg}deg`
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old WebKit gradient too
|
||||
*/
|
||||
old(prefix) {
|
||||
if (prefix === '-webkit-') {
|
||||
let type
|
||||
if (this.name === 'linear-gradient') {
|
||||
type = 'linear'
|
||||
} else if (this.name === 'repeating-linear-gradient') {
|
||||
type = 'repeating-linear'
|
||||
} else if (this.name === 'repeating-radial-gradient') {
|
||||
type = 'repeating-radial'
|
||||
} else {
|
||||
type = 'radial'
|
||||
}
|
||||
let string = '-gradient'
|
||||
let regexp = utils.regexp(
|
||||
`-webkit-(${type}-gradient|gradient\\(\\s*${type})`,
|
||||
false
|
||||
)
|
||||
|
||||
return new OldValue(this.name, prefix + this.name, string, regexp)
|
||||
} else {
|
||||
return super.old(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change direction syntax to old webkit
|
||||
*/
|
||||
oldDirection(params) {
|
||||
let div = this.cloneDiv(params[0])
|
||||
|
||||
if (!params[0][0] || params[0][0].value !== 'to') {
|
||||
return params.unshift([
|
||||
{ type: 'word', value: Gradient.oldDirections.bottom },
|
||||
div
|
||||
])
|
||||
} else {
|
||||
let words = []
|
||||
for (let node of params[0].slice(2)) {
|
||||
if (node.type === 'word') {
|
||||
words.push(node.value.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
||||
words = words.join(' ')
|
||||
let old = Gradient.oldDirections[words] || words
|
||||
|
||||
params[0] = [{ type: 'word', value: old }, div]
|
||||
return params[0]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to old webkit syntax
|
||||
*/
|
||||
oldWebkit(node, decl, result) {
|
||||
let { nodes } = node
|
||||
let string = parser.stringify(node.nodes)
|
||||
|
||||
if (this.name !== 'linear-gradient') {
|
||||
return false
|
||||
}
|
||||
if (nodes.length === 0) {
|
||||
return false
|
||||
}
|
||||
if (nodes[0] && nodes[0].value.includes('deg')) {
|
||||
return false
|
||||
}
|
||||
if (
|
||||
string.includes('px') ||
|
||||
string.includes('-corner') ||
|
||||
string.includes('-side')
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (string.includes('var(')) {
|
||||
return false
|
||||
}
|
||||
|
||||
let params = [[]]
|
||||
for (let i of nodes) {
|
||||
params[params.length - 1].push(i)
|
||||
if (i.type === 'div' && i.value === ',') {
|
||||
params.push([])
|
||||
}
|
||||
}
|
||||
|
||||
this.oldDirection(params)
|
||||
for (let param of params.slice(1)) {
|
||||
let tokens = param.filter(i => i.type === 'word' || i.type === 'function')
|
||||
if (
|
||||
tokens.length === 1 &&
|
||||
((tokens[0].type === 'word' && parser.unit(tokens[0].value)) ||
|
||||
(tokens[0].type === 'function' &&
|
||||
/^(calc|min|max|clamp)$/i.test(tokens[0].value)))
|
||||
) {
|
||||
if (decl && result) {
|
||||
decl.warn(
|
||||
result,
|
||||
'Gradient color hints are not supported by old WebKit'
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
this.colorStops(params)
|
||||
|
||||
node.nodes = []
|
||||
for (let param of params) {
|
||||
node.nodes.push(...param)
|
||||
}
|
||||
|
||||
node.nodes.unshift(
|
||||
{ type: 'word', value: 'linear' },
|
||||
this.cloneDiv(node.nodes)
|
||||
)
|
||||
node.value = '-webkit-gradient'
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Change degrees for webkit prefix
|
||||
*/
|
||||
replace(string, prefix, decl, result) {
|
||||
let ast = parser(string)
|
||||
for (let node of ast.nodes) {
|
||||
let gradientName = this.name // gradient name
|
||||
if (node.type === 'function' && node.value === gradientName) {
|
||||
node.nodes = this.newDirection(node.nodes)
|
||||
node.nodes = this.normalize(node.nodes, gradientName)
|
||||
if (prefix === '-webkit- old') {
|
||||
let changes = this.oldWebkit(node, decl, result)
|
||||
if (!changes) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
node.nodes = this.convertDirection(node.nodes)
|
||||
node.value = prefix + node.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return ast.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace first token
|
||||
*/
|
||||
replaceFirst(params, ...words) {
|
||||
let prefix = words.map(i => {
|
||||
if (i === ' ') {
|
||||
return { type: 'space', value: i }
|
||||
}
|
||||
return { type: 'word', value: i }
|
||||
})
|
||||
return prefix.concat(params.slice(1))
|
||||
}
|
||||
|
||||
revertDirection(word) {
|
||||
return Gradient.directions[word.toLowerCase()] || word
|
||||
}
|
||||
|
||||
/**
|
||||
* Round float and save digits under dot
|
||||
*/
|
||||
roundFloat(float, digits) {
|
||||
return parseFloat(float.toFixed(digits))
|
||||
}
|
||||
}
|
||||
|
||||
Gradient.names = [
|
||||
'linear-gradient',
|
||||
'repeating-linear-gradient',
|
||||
'radial-gradient',
|
||||
'repeating-radial-gradient'
|
||||
]
|
||||
|
||||
Gradient.directions = {
|
||||
bottom: 'top',
|
||||
left: 'right',
|
||||
right: 'left',
|
||||
top: 'bottom' // default value
|
||||
}
|
||||
|
||||
// Direction to replace
|
||||
Gradient.oldDirections = {
|
||||
'bottom': 'left top, left bottom',
|
||||
'bottom left': 'right top, left bottom',
|
||||
'bottom right': 'left top, right bottom',
|
||||
'left': 'right top, left top',
|
||||
|
||||
'left bottom': 'right top, left bottom',
|
||||
'left top': 'right bottom, left top',
|
||||
'right': 'left top, right top',
|
||||
'right bottom': 'left top, right bottom',
|
||||
'right top': 'left bottom, right top',
|
||||
'top': 'left bottom, left top',
|
||||
'top left': 'right bottom, left top',
|
||||
'top right': 'left bottom, right top'
|
||||
}
|
||||
|
||||
module.exports = Gradient
|
||||
@@ -0,0 +1,52 @@
|
||||
let Declaration = require('../declaration')
|
||||
let { isPureNumber } = require('../utils')
|
||||
|
||||
class GridEnd extends Declaration {
|
||||
/**
|
||||
* Change repeating syntax for IE
|
||||
*/
|
||||
insert(decl, prefix, prefixes, result) {
|
||||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
|
||||
|
||||
let clonedDecl = this.clone(decl)
|
||||
|
||||
let startProp = decl.prop.replace(/end$/, 'start')
|
||||
let spanProp = prefix + decl.prop.replace(/end$/, 'span')
|
||||
|
||||
if (decl.parent.some(i => i.prop === spanProp)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
clonedDecl.prop = spanProp
|
||||
|
||||
if (decl.value.includes('span')) {
|
||||
clonedDecl.value = decl.value.replace(/span\s/i, '')
|
||||
} else {
|
||||
let startDecl
|
||||
decl.parent.walkDecls(startProp, d => {
|
||||
startDecl = d
|
||||
})
|
||||
if (startDecl) {
|
||||
if (isPureNumber(startDecl.value)) {
|
||||
let value = Number(decl.value) - Number(startDecl.value) + ''
|
||||
clonedDecl.value = value
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
} else {
|
||||
decl.warn(
|
||||
result,
|
||||
`Can not prefix ${decl.prop} (${startProp} is not found)`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
decl.cloneBefore(clonedDecl)
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
GridEnd.names = ['grid-row-end', 'grid-column-end']
|
||||
|
||||
module.exports = GridEnd
|
||||
@@ -0,0 +1,125 @@
|
||||
let Declaration = require('../declaration')
|
||||
let Processor = require('../processor')
|
||||
let {
|
||||
autoplaceGridItems,
|
||||
getGridGap,
|
||||
inheritGridGap,
|
||||
prefixTrackProp,
|
||||
prefixTrackValue
|
||||
} = require('./grid-utils')
|
||||
|
||||
class GridRowsColumns extends Declaration {
|
||||
insert(decl, prefix, prefixes, result) {
|
||||
if (prefix !== '-ms-') return super.insert(decl, prefix, prefixes)
|
||||
|
||||
let { parent, prop, value } = decl
|
||||
let isRowProp = prop.includes('rows')
|
||||
let isColumnProp = prop.includes('columns')
|
||||
|
||||
let hasGridTemplate = parent.some(
|
||||
i => i.prop === 'grid-template' || i.prop === 'grid-template-areas'
|
||||
)
|
||||
|
||||
/**
|
||||
* Not to prefix rows declaration if grid-template(-areas) is present
|
||||
*/
|
||||
if (hasGridTemplate && isRowProp) {
|
||||
return false
|
||||
}
|
||||
|
||||
let processor = new Processor({ options: {} })
|
||||
let status = processor.gridStatus(parent, result)
|
||||
let gap = getGridGap(decl)
|
||||
gap = inheritGridGap(decl, gap) || gap
|
||||
|
||||
let gapValue = isRowProp ? gap.row : gap.column
|
||||
|
||||
if ((status === 'no-autoplace' || status === true) && !hasGridTemplate) {
|
||||
gapValue = null
|
||||
}
|
||||
|
||||
let prefixValue = prefixTrackValue({
|
||||
gap: gapValue,
|
||||
value
|
||||
})
|
||||
|
||||
/**
|
||||
* Insert prefixes
|
||||
*/
|
||||
decl.cloneBefore({
|
||||
prop: prefixTrackProp({ prefix, prop }),
|
||||
value: prefixValue
|
||||
})
|
||||
|
||||
let autoflow = parent.nodes.find(i => i.prop === 'grid-auto-flow')
|
||||
let autoflowValue = 'row'
|
||||
|
||||
if (autoflow && !processor.disabled(autoflow, result)) {
|
||||
autoflowValue = autoflow.value.trim()
|
||||
}
|
||||
if (status === 'autoplace') {
|
||||
/**
|
||||
* Show warning if grid-template-rows decl is not found
|
||||
*/
|
||||
let rowDecl = parent.nodes.find(i => i.prop === 'grid-template-rows')
|
||||
|
||||
if (!rowDecl && hasGridTemplate) {
|
||||
return undefined
|
||||
} else if (!rowDecl && !hasGridTemplate) {
|
||||
decl.warn(
|
||||
result,
|
||||
'Autoplacement does not work without grid-template-rows property'
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Show warning if grid-template-columns decl is not found
|
||||
*/
|
||||
let columnDecl = parent.nodes.find(i => {
|
||||
return i.prop === 'grid-template-columns'
|
||||
})
|
||||
if (!columnDecl && !hasGridTemplate) {
|
||||
decl.warn(
|
||||
result,
|
||||
'Autoplacement does not work without grid-template-columns property'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoplace grid items
|
||||
*/
|
||||
if (isColumnProp && !hasGridTemplate) {
|
||||
autoplaceGridItems(decl, result, gap, autoflowValue)
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Change IE property back
|
||||
*/
|
||||
normalize(prop) {
|
||||
return prop.replace(/^grid-(rows|columns)/, 'grid-template-$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* Change property name for IE
|
||||
*/
|
||||
prefixed(prop, prefix) {
|
||||
if (prefix === '-ms-') {
|
||||
return prefixTrackProp({ prefix, prop })
|
||||
}
|
||||
return super.prefixed(prop, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
GridRowsColumns.names = [
|
||||
'grid-template-rows',
|
||||
'grid-template-columns',
|
||||
'grid-rows',
|
||||
'grid-columns'
|
||||
]
|
||||
|
||||
module.exports = GridRowsColumns
|
||||
+7
-19
@@ -604,8 +604,7 @@ function insertAreas(css, isDisabled) {
|
||||
Object.assign(i, {
|
||||
raws: {
|
||||
between: gridArea.raws.between
|
||||
},
|
||||
source: gridArea.source
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -627,8 +626,7 @@ function insertAreas(css, isDisabled) {
|
||||
Object.assign(i, {
|
||||
raws: {
|
||||
between: gridArea.raws.between
|
||||
},
|
||||
source: gridArea.source
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -663,8 +661,7 @@ function insertAreas(css, isDisabled) {
|
||||
Object.assign(i, {
|
||||
raws: {
|
||||
between: gridArea.raws.between
|
||||
},
|
||||
source: gridArea.source
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -686,8 +683,7 @@ function insertAreas(css, isDisabled) {
|
||||
Object.assign(i, {
|
||||
raws: {
|
||||
between: gridArea.raws.between
|
||||
},
|
||||
source: gridArea.source
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@@ -736,11 +732,7 @@ function insertAreas(css, isDisabled) {
|
||||
.filter(p => p !== 'lastRule')
|
||||
.forEach(params => {
|
||||
if (data[params].length > 0 && lastRule) {
|
||||
lastRule.after({
|
||||
name: 'media',
|
||||
params,
|
||||
source: data[params][0].source
|
||||
})
|
||||
lastRule.after({ name: 'media', params })
|
||||
lastRule.next().append(data[params])
|
||||
}
|
||||
})
|
||||
@@ -1172,12 +1164,8 @@ function autoplaceGridItems(decl, result, gap, autoflowValue = 'row') {
|
||||
node.selector = nodeSelector
|
||||
|
||||
// insert prefixed row/column values
|
||||
node.append({ prop: '-ms-grid-row', source: decl.source, value: row.start })
|
||||
node.append({
|
||||
prop: '-ms-grid-column',
|
||||
source: decl.source,
|
||||
value: column.start
|
||||
})
|
||||
node.append({ prop: '-ms-grid-row', value: row.start })
|
||||
node.append({ prop: '-ms-grid-column', value: column.start })
|
||||
|
||||
// insert rule
|
||||
parent.after(node)
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
let { list } = require('postcss')
|
||||
let parser = require('postcss-value-parser')
|
||||
|
||||
let Browsers = require('./browsers')
|
||||
let vendor = require('./vendor')
|
||||
|
||||
class Transition {
|
||||
constructor(prefixes) {
|
||||
this.props = ['transition', 'transition-property']
|
||||
this.prefixes = prefixes
|
||||
}
|
||||
|
||||
/**
|
||||
* Process transition and add prefixes for all necessary properties
|
||||
*/
|
||||
add(decl, result) {
|
||||
let prefix, prop
|
||||
let add = this.prefixes.add[decl.prop]
|
||||
let vendorPrefixes = this.ruleVendorPrefixes(decl)
|
||||
let declPrefixes = vendorPrefixes || (add && add.prefixes) || []
|
||||
|
||||
let params = this.parse(decl.value)
|
||||
let names = params.map(i => this.findProp(i))
|
||||
let added = []
|
||||
|
||||
if (names.some(i => i[0] === '-')) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let param of params) {
|
||||
prop = this.findProp(param)
|
||||
if (prop[0] === '-') continue
|
||||
|
||||
let prefixer = this.prefixes.add[prop]
|
||||
if (!prefixer || !prefixer.prefixes) continue
|
||||
|
||||
for (prefix of prefixer.prefixes) {
|
||||
if (vendorPrefixes && !vendorPrefixes.some(p => prefix.includes(p))) {
|
||||
continue
|
||||
}
|
||||
|
||||
let prefixed = this.prefixes.prefixed(prop, prefix)
|
||||
if (prefixed !== '-ms-transform' && !names.includes(prefixed)) {
|
||||
if (!this.disabled(prop, prefix)) {
|
||||
added.push(this.clone(prop, prefixed, param))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params = params.concat(added)
|
||||
let value = this.stringify(params)
|
||||
|
||||
let webkitClean = this.stringify(
|
||||
this.cleanFromUnprefixed(params, '-webkit-')
|
||||
)
|
||||
if (declPrefixes.includes('-webkit-')) {
|
||||
this.cloneBefore(decl, `-webkit-${decl.prop}`, webkitClean)
|
||||
}
|
||||
this.cloneBefore(decl, decl.prop, webkitClean)
|
||||
if (declPrefixes.includes('-o-')) {
|
||||
let operaClean = this.stringify(this.cleanFromUnprefixed(params, '-o-'))
|
||||
this.cloneBefore(decl, `-o-${decl.prop}`, operaClean)
|
||||
}
|
||||
|
||||
for (prefix of declPrefixes) {
|
||||
if (prefix !== '-webkit-' && prefix !== '-o-') {
|
||||
let prefixValue = this.stringify(
|
||||
this.cleanOtherPrefixes(params, prefix)
|
||||
)
|
||||
this.cloneBefore(decl, prefix + decl.prop, prefixValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (value !== decl.value && !this.already(decl, decl.prop, value)) {
|
||||
this.checkForWarning(result, decl)
|
||||
decl.cloneBefore()
|
||||
decl.value = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does we already have this declaration
|
||||
*/
|
||||
already(decl, prop, value) {
|
||||
return decl.parent.some(i => i.prop === prop && i.value === value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Show transition-property warning
|
||||
*/
|
||||
checkForWarning(result, decl) {
|
||||
if (decl.prop !== 'transition-property') {
|
||||
return
|
||||
}
|
||||
|
||||
let isPrefixed = false
|
||||
let hasAssociatedProp = false
|
||||
|
||||
decl.parent.each(i => {
|
||||
if (i.type !== 'decl') {
|
||||
return undefined
|
||||
}
|
||||
if (i.prop.indexOf('transition-') !== 0) {
|
||||
return undefined
|
||||
}
|
||||
let values = list.comma(i.value)
|
||||
// check if current Rule's transition-property comma separated value list needs prefixes
|
||||
if (i.prop === 'transition-property') {
|
||||
values.forEach(value => {
|
||||
let lookup = this.prefixes.add[value]
|
||||
if (lookup && lookup.prefixes && lookup.prefixes.length > 0) {
|
||||
isPrefixed = true
|
||||
}
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
// check if another transition-* prop in current Rule has comma separated value list
|
||||
hasAssociatedProp = hasAssociatedProp || values.length > 1
|
||||
return false
|
||||
})
|
||||
|
||||
if (isPrefixed && hasAssociatedProp) {
|
||||
decl.warn(
|
||||
result,
|
||||
'Replace transition-property to transition, ' +
|
||||
'because Autoprefixer could not support ' +
|
||||
'any cases of transition-property ' +
|
||||
'and other transition-*'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all non-webkit prefixes and unprefixed params if we have prefixed
|
||||
*/
|
||||
cleanFromUnprefixed(params, prefix) {
|
||||
let remove = params
|
||||
.map(i => this.findProp(i))
|
||||
.filter(i => i.slice(0, prefix.length) === prefix)
|
||||
.map(i => this.prefixes.unprefixed(i))
|
||||
|
||||
let result = []
|
||||
for (let param of params) {
|
||||
let prop = this.findProp(param)
|
||||
let p = vendor.prefix(prop)
|
||||
if (!remove.includes(prop) && (p === prefix || p === '')) {
|
||||
result.push(param)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
cleanOtherPrefixes(params, prefix) {
|
||||
return params.filter(param => {
|
||||
let current = vendor.prefix(this.findProp(param))
|
||||
return current === '' || current === prefix
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new param array with different name
|
||||
*/
|
||||
clone(origin, name, param) {
|
||||
let result = []
|
||||
let changed = false
|
||||
for (let i of param) {
|
||||
if (!changed && i.type === 'word' && i.value === origin) {
|
||||
result.push({ type: 'word', value: name })
|
||||
changed = true
|
||||
} else {
|
||||
result.push(i)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Add declaration if it is not exist
|
||||
*/
|
||||
cloneBefore(decl, prop, value) {
|
||||
if (!this.already(decl, prop, value)) {
|
||||
decl.cloneBefore({ prop, value })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check property for disabled by option
|
||||
*/
|
||||
disabled(prop, prefix) {
|
||||
let other = ['order', 'justify-content', 'align-self', 'align-content']
|
||||
if (prop.includes('flex') || other.includes(prop)) {
|
||||
if (this.prefixes.options.flexbox === false) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.prefixes.options.flexbox === 'no-2009') {
|
||||
return prefix.includes('2009')
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create separator
|
||||
*/
|
||||
div(params) {
|
||||
for (let param of params) {
|
||||
for (let node of param) {
|
||||
if (node.type === 'div' && node.value === ',') {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
return { after: ' ', type: 'div', value: ',' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Find property name
|
||||
*/
|
||||
findProp(param) {
|
||||
let prop = param[0].value
|
||||
if (/^\d/.test(prop)) {
|
||||
for (let [i, token] of param.entries()) {
|
||||
if (i !== 0 && token.type === 'word') {
|
||||
return token.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return prop
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse properties list to array
|
||||
*/
|
||||
parse(value) {
|
||||
let ast = parser(value)
|
||||
let result = []
|
||||
let param = []
|
||||
for (let node of ast.nodes) {
|
||||
param.push(node)
|
||||
if (node.type === 'div' && node.value === ',') {
|
||||
result.push(param)
|
||||
param = []
|
||||
}
|
||||
}
|
||||
result.push(param)
|
||||
return result.filter(i => i.length > 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process transition and remove all unnecessary properties
|
||||
*/
|
||||
remove(decl) {
|
||||
let params = this.parse(decl.value)
|
||||
params = params.filter(i => {
|
||||
let prop = this.prefixes.remove[this.findProp(i)]
|
||||
return !prop || !prop.remove
|
||||
})
|
||||
let value = this.stringify(params)
|
||||
|
||||
if (decl.value === value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (params.length === 0) {
|
||||
decl.remove()
|
||||
return
|
||||
}
|
||||
|
||||
let double = decl.parent.some(i => {
|
||||
return i.prop === decl.prop && i.value === value
|
||||
})
|
||||
let smaller = decl.parent.some(i => {
|
||||
return i !== decl && i.prop === decl.prop && i.value.length > value.length
|
||||
})
|
||||
|
||||
if (double || smaller) {
|
||||
decl.remove()
|
||||
return
|
||||
}
|
||||
|
||||
decl.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if transition prop is inside vendor specific rule
|
||||
*/
|
||||
ruleVendorPrefixes(decl) {
|
||||
let { parent } = decl
|
||||
|
||||
if (parent.type !== 'rule') {
|
||||
return false
|
||||
} else if (!parent.selector.includes(':-')) {
|
||||
return false
|
||||
}
|
||||
|
||||
let selectors = Browsers.prefixes().filter(s =>
|
||||
parent.selector.includes(':' + s)
|
||||
)
|
||||
|
||||
return selectors.length > 0 ? selectors : false
|
||||
}
|
||||
|
||||
/**
|
||||
* Return properties string from array
|
||||
*/
|
||||
stringify(params) {
|
||||
if (params.length === 0) {
|
||||
return ''
|
||||
}
|
||||
let nodes = []
|
||||
for (let param of params) {
|
||||
if (param[param.length - 1].type !== 'div') {
|
||||
param.push(this.div(params))
|
||||
}
|
||||
nodes.push(...param)
|
||||
}
|
||||
if (nodes[0].type === 'div') {
|
||||
nodes = nodes.slice(1)
|
||||
}
|
||||
if (nodes[nodes.length - 1].type === 'div') {
|
||||
nodes = nodes.slice(0, -1)
|
||||
}
|
||||
return parser.stringify({ nodes })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transition
|
||||
@@ -0,0 +1,93 @@
|
||||
let { list } = require('postcss')
|
||||
|
||||
/**
|
||||
* Throw special error, to tell beniary,
|
||||
* that this error is from Autoprefixer.
|
||||
*/
|
||||
module.exports.error = function (text) {
|
||||
let err = new Error(text)
|
||||
err.autoprefixer = true
|
||||
throw err
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array, that doesn’t contain duplicates.
|
||||
*/
|
||||
module.exports.uniq = function (array) {
|
||||
return [...new Set(array)]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return "-webkit-" on "-webkit- old"
|
||||
*/
|
||||
module.exports.removeNote = function (string) {
|
||||
if (!string.includes(' ')) {
|
||||
return string
|
||||
}
|
||||
|
||||
return string.split(' ')[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape RegExp symbols
|
||||
*/
|
||||
module.exports.escapeRegexp = function (string) {
|
||||
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
|
||||
}
|
||||
|
||||
/**
|
||||
* Return regexp to check, that CSS string contain word
|
||||
*/
|
||||
module.exports.regexp = function (word, escape = true) {
|
||||
if (escape) {
|
||||
word = this.escapeRegexp(word)
|
||||
}
|
||||
return new RegExp(`(^|[\\s,(])(${word}($|[\\s(,]))`, 'gi')
|
||||
}
|
||||
|
||||
/**
|
||||
* Change comma list
|
||||
*/
|
||||
module.exports.editList = function (value, callback) {
|
||||
let origin = list.comma(value)
|
||||
let changed = callback(origin, [])
|
||||
|
||||
if (origin === changed) {
|
||||
return value
|
||||
}
|
||||
|
||||
let join = value.match(/,\s*/)
|
||||
join = join ? join[0] : ', '
|
||||
return changed.join(join)
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the selector into parts.
|
||||
* It returns 3 level deep array because selectors can be comma
|
||||
* separated (1), space separated (2), and combined (3)
|
||||
* @param {String} selector selector string
|
||||
* @return {Array<Array<Array>>} 3 level deep array of split selector
|
||||
* @see utils.test.js for examples
|
||||
*/
|
||||
module.exports.splitSelector = function (selector) {
|
||||
return list.comma(selector).map(i => {
|
||||
return list.space(i).map(k => {
|
||||
return k.split(/(?=\.|#)/g)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a given value only contains numbers.
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
module.exports.isPureNumber = function (value) {
|
||||
if (typeof value === 'number') {
|
||||
return true
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return /^[0-9]+$/.test(value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in new issue
Block a user