taro-s4s
Version:
Nerv-multi多端开发解决方案
206 lines (179 loc) • 5.57 kB
JavaScript
var postcss = require('postcss')
var objectAssign = require('object-assign')
var pxRegex = require('./lib/pixel-unit-regex')
var filterPropList = require('./lib/filter-prop-list')
var defaults = {
rootValue: 16,
unitPrecision: 5,
selectorBlackList: [],
propList: ['*'],
replace: true,
mediaQuery: false,
minPixelValue: 0
}
var legacyOptions = {
'root_value': 'rootValue',
'unit_precision': 'unitPrecision',
'selector_black_list': 'selectorBlackList',
'prop_white_list': 'propList',
'media_query': 'mediaQuery',
'propWhiteList': 'propList'
}
const deviceRatio = {
'640': 2.34 / 2,
'750': 1,
'828': 1.81 / 2
}
const baseFontSize = 40
const DEFAULT_WEAPP_OPTIONS = {
platform: 'weapp',
designWidth: 750,
deviceRatio,
}
var targetUnit
module.exports = postcss.plugin('postcss-pxtransform', function (options) {
options = Object.assign(DEFAULT_WEAPP_OPTIONS, options || {});
switch (options.platform) {
case 'aliapp':
case 'weapp': {
options.rootValue = options.deviceRatio[options.designWidth]
targetUnit = 'rpx'
break
}
case 'h5': {
options.rootValue = baseFontSize * options.designWidth / 640
targetUnit = 'rem'
break
}
}
convertLegacyOptions(options)
var opts = objectAssign({}, defaults, options)
var pxReplace = createPxReplace(opts.rootValue, opts.unitPrecision,
opts.minPixelValue)
var satisfyPropList = createPropListMatcher(opts.propList)
return function (css) {
for (var i = 0; i < css.nodes.length; i++) {
if (css.nodes[i].type === 'comment') {
if (css.nodes[i].text === 'postcss-pxtransform disable') {
return
} else {
break
}
}
}
css.walkDecls(function (decl, i) {
// This should be the fastest test and will remove most declarations
if (decl.value.indexOf('px') === -1) return
if (!satisfyPropList(decl.prop)) return
if (blacklistedSelector(opts.selectorBlackList,
decl.parent.selector)) return
var value = decl.value.replace(pxRegex, pxReplace)
// if rem unit already exists, do not add or replace
if (declarationExists(decl.parent, decl.prop, value)) return
if (opts.replace) {
decl.value = value
} else {
decl.parent.insertAfter(i, decl.clone({value: value}))
}
})
if (opts.mediaQuery) {
css.walkAtRules('media', function (rule) {
if (rule.params.indexOf('px') === -1) return
rule.params = rule.params.replace(pxRegex, pxReplace)
})
}
}
})
function convertLegacyOptions (options) {
if (typeof options !== 'object') return
if (
(
(typeof options['prop_white_list'] !== 'undefined' &&
options['prop_white_list'].length === 0) ||
(typeof options.propWhiteList !== 'undefined' &&
options.propWhiteList.length === 0)
) &&
typeof options.propList === 'undefined'
) {
options.propList = ['*']
delete options['prop_white_list']
delete options.propWhiteList
}
Object.keys(legacyOptions).forEach(function (key) {
if (options.hasOwnProperty(key)) {
options[legacyOptions[key]] = options[key]
delete options[key]
}
})
}
function createPxReplace (rootValue, unitPrecision, minPixelValue) {
return function (m, $1) {
if (!$1) return m
var pixels = parseFloat($1)
if (pixels < minPixelValue) return m
var fixedVal = toFixed((pixels / rootValue), unitPrecision)
return (fixedVal === 0) ? '0' : fixedVal + targetUnit
}
}
function toFixed (number, precision) {
var multiplier = Math.pow(10, precision + 1)
var wholeNumber = Math.floor(number * multiplier)
return Math.round(wholeNumber / 10) * 10 / multiplier
}
function declarationExists (decls, prop, value) {
return decls.some(function (decl) {
return (decl.prop === prop && decl.value === value)
})
}
function blacklistedSelector (blacklist, selector) {
if (typeof selector !== 'string') return
return blacklist.some(function (regex) {
if (typeof regex === 'string') return selector.indexOf(regex) !== -1
return selector.match(regex)
})
}
function createPropListMatcher (propList) {
var hasWild = propList.indexOf('*') > -1
var matchAll = (hasWild && propList.length === 1)
var lists = {
exact: filterPropList.exact(propList),
contain: filterPropList.contain(propList),
startWith: filterPropList.startWith(propList),
endWith: filterPropList.endWith(propList),
notExact: filterPropList.notExact(propList),
notContain: filterPropList.notContain(propList),
notStartWith: filterPropList.notStartWith(propList),
notEndWith: filterPropList.notEndWith(propList)
}
return function (prop) {
if (matchAll) return true
return (
(
hasWild ||
lists.exact.indexOf(prop) > -1 ||
lists.contain.some(function (m) {
return prop.indexOf(m) > -1
}) ||
lists.startWith.some(function (m) {
return prop.indexOf(m) === 0
}) ||
lists.endWith.some(function (m) {
return prop.indexOf(m) === prop.length - m.length
})
) &&
!(
lists.notExact.indexOf(prop) > -1 ||
lists.notContain.some(function (m) {
return prop.indexOf(m) > -1
}) ||
lists.notStartWith.some(function (m) {
return prop.indexOf(m) === 0
}) ||
lists.notEndWith.some(function (m) {
return prop.indexOf(m) === prop.length - m.length
})
)
)
}
}