1 | 'use strict'
|
2 |
|
3 | var postcss = require('postcss')
|
4 | var objectAssign = require('object-assign')
|
5 | var pxRegex = require('./lib/pixel-unit-regex')
|
6 | var filterPropList = require('./lib/filter-prop-list')
|
7 |
|
8 | var defaults = {
|
9 | rootValue: 16,
|
10 | unitPrecision: 5,
|
11 | selectorBlackList: [],
|
12 | propList: ['*'],
|
13 | replace: true,
|
14 | mediaQuery: false,
|
15 | minPixelValue: 0
|
16 | }
|
17 |
|
18 | var legacyOptions = {
|
19 | 'root_value': 'rootValue',
|
20 | 'unit_precision': 'unitPrecision',
|
21 | 'selector_black_list': 'selectorBlackList',
|
22 | 'prop_white_list': 'propList',
|
23 | 'media_query': 'mediaQuery',
|
24 | 'propWhiteList': 'propList'
|
25 | }
|
26 |
|
27 | const deviceRatio = {
|
28 | '640': 2.34 / 2,
|
29 | '750': 1,
|
30 | '828': 1.81 / 2
|
31 | }
|
32 |
|
33 | const baseFontSize = 40
|
34 |
|
35 | const DEFAULT_WEAPP_OPTIONS = {
|
36 | platform: 'weapp',
|
37 | designWidth: 750,
|
38 | deviceRatio
|
39 | }
|
40 |
|
41 | var targetUnit
|
42 |
|
43 | module.exports = postcss.plugin('postcss-pxtransform', function (options) {
|
44 | options = Object.assign(DEFAULT_WEAPP_OPTIONS, options || {})
|
45 |
|
46 | switch (options.platform) {
|
47 | case 'weapp': {
|
48 | options.rootValue = options.deviceRatio[options.designWidth]
|
49 | targetUnit = 'rpx'
|
50 | break
|
51 | }
|
52 | case 'h5': {
|
53 | options.rootValue = baseFontSize * options.designWidth / 640
|
54 | targetUnit = 'rem'
|
55 | break
|
56 | }
|
57 | case 'rn': {
|
58 | options.rootValue = options.deviceRatio[options.designWidth] * 2
|
59 | targetUnit = 'px'
|
60 | break
|
61 | }
|
62 | }
|
63 |
|
64 | convertLegacyOptions(options)
|
65 |
|
66 | var opts = objectAssign({}, defaults, options)
|
67 | var onePxTransform = typeof options.onePxTransform === 'undefined' ? true : options.onePxTransform
|
68 | var pxReplace = createPxReplace(opts.rootValue, opts.unitPrecision,
|
69 | opts.minPixelValue, onePxTransform)
|
70 |
|
71 | var satisfyPropList = createPropListMatcher(opts.propList)
|
72 |
|
73 | return function (css) {
|
74 | for (var i = 0; i < css.nodes.length; i++) {
|
75 | if (css.nodes[i].type === 'comment') {
|
76 | if (css.nodes[i].text === 'postcss-pxtransform disable') {
|
77 | return
|
78 | } else {
|
79 | break
|
80 | }
|
81 | }
|
82 | }
|
83 |
|
84 |
|
85 | if (options.platform === 'rn') {
|
86 | css.walkComments(comment => {
|
87 | if (comment.text === 'postcss-pxtransform rn eject enable') {
|
88 | let next = comment.next()
|
89 | while (next) {
|
90 | if (next.type === 'comment' && next.text === 'postcss-pxtransform rn eject disable') {
|
91 | break
|
92 | }
|
93 | const temp = next.next()
|
94 | next.remove()
|
95 | next = temp
|
96 | }
|
97 | }
|
98 | })
|
99 | }
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | css.walkComments(comment => {
|
105 | const wordList = comment.text.split(' ')
|
106 |
|
107 | if (wordList.indexOf('#ifdef') > -1) {
|
108 |
|
109 | if (wordList.indexOf(options.platform) === -1) {
|
110 | let next = comment.next()
|
111 | while (next) {
|
112 | if (next.type === 'comment' && next.text.trim() === '#endif') {
|
113 | break
|
114 | }
|
115 | const temp = next.next()
|
116 | next.remove()
|
117 | next = temp
|
118 | }
|
119 | }
|
120 | }
|
121 | })
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | css.walkComments(comment => {
|
127 | const wordList = comment.text.split(' ')
|
128 |
|
129 | if (wordList.indexOf('#ifndef') > -1) {
|
130 |
|
131 | if (wordList.indexOf(options.platform) > -1) {
|
132 | let next = comment.next()
|
133 | while (next) {
|
134 | if (next.type === 'comment' && next.text.trim() === '#endif') {
|
135 | break
|
136 | }
|
137 | const temp = next.next()
|
138 | next.remove()
|
139 | next = temp
|
140 | }
|
141 | }
|
142 | }
|
143 | })
|
144 |
|
145 | css.walkDecls(function (decl, i) {
|
146 |
|
147 | if (decl.value.indexOf('px') === -1) return
|
148 |
|
149 | if (!satisfyPropList(decl.prop)) return
|
150 |
|
151 | if (blacklistedSelector(opts.selectorBlackList,
|
152 | decl.parent.selector)) return
|
153 |
|
154 | var value = decl.value.replace(pxRegex, pxReplace)
|
155 |
|
156 |
|
157 | if (declarationExists(decl.parent, decl.prop, value)) return
|
158 |
|
159 | if (opts.replace) {
|
160 | decl.value = value
|
161 | } else {
|
162 | decl.parent.insertAfter(i, decl.clone({value: value}))
|
163 | }
|
164 | })
|
165 |
|
166 | if (opts.mediaQuery) {
|
167 | css.walkAtRules('media', function (rule) {
|
168 | if (rule.params.indexOf('px') === -1) return
|
169 | rule.params = rule.params.replace(pxRegex, pxReplace)
|
170 | })
|
171 | }
|
172 | }
|
173 | })
|
174 |
|
175 | function convertLegacyOptions (options) {
|
176 | if (typeof options !== 'object') return
|
177 | if (
|
178 | (
|
179 | (typeof options['prop_white_list'] !== 'undefined' &&
|
180 | options['prop_white_list'].length === 0) ||
|
181 | (typeof options.propWhiteList !== 'undefined' &&
|
182 | options.propWhiteList.length === 0)
|
183 | ) &&
|
184 | typeof options.propList === 'undefined'
|
185 | ) {
|
186 | options.propList = ['*']
|
187 | delete options['prop_white_list']
|
188 | delete options.propWhiteList
|
189 | }
|
190 | Object.keys(legacyOptions).forEach(function (key) {
|
191 | if (options.hasOwnProperty(key)) {
|
192 | options[legacyOptions[key]] = options[key]
|
193 | delete options[key]
|
194 | }
|
195 | })
|
196 | }
|
197 |
|
198 | function createPxReplace (rootValue, unitPrecision, minPixelValue, onePxTransform) {
|
199 | return function (m, $1) {
|
200 | if (!$1) return m
|
201 | if (!onePxTransform && parseInt($1, 10) === 1) {
|
202 | return m
|
203 | }
|
204 | var pixels = parseFloat($1)
|
205 | if (pixels < minPixelValue) return m
|
206 | var fixedVal = toFixed((pixels / rootValue), unitPrecision)
|
207 | return (fixedVal === 0) ? '0' : fixedVal + targetUnit
|
208 | }
|
209 | }
|
210 |
|
211 | function toFixed (number, precision) {
|
212 | var multiplier = Math.pow(10, precision + 1)
|
213 | var wholeNumber = Math.floor(number * multiplier)
|
214 | return Math.round(wholeNumber / 10) * 10 / multiplier
|
215 | }
|
216 |
|
217 | function declarationExists (decls, prop, value) {
|
218 | return decls.some(function (decl) {
|
219 | return (decl.prop === prop && decl.value === value)
|
220 | })
|
221 | }
|
222 |
|
223 | function blacklistedSelector (blacklist, selector) {
|
224 | if (typeof selector !== 'string') return
|
225 | return blacklist.some(function (regex) {
|
226 | if (typeof regex === 'string') return selector.indexOf(regex) !== -1
|
227 | return selector.match(regex)
|
228 | })
|
229 | }
|
230 |
|
231 | function createPropListMatcher (propList) {
|
232 | var hasWild = propList.indexOf('*') > -1
|
233 | var matchAll = (hasWild && propList.length === 1)
|
234 | var lists = {
|
235 | exact: filterPropList.exact(propList),
|
236 | contain: filterPropList.contain(propList),
|
237 | startWith: filterPropList.startWith(propList),
|
238 | endWith: filterPropList.endWith(propList),
|
239 | notExact: filterPropList.notExact(propList),
|
240 | notContain: filterPropList.notContain(propList),
|
241 | notStartWith: filterPropList.notStartWith(propList),
|
242 | notEndWith: filterPropList.notEndWith(propList)
|
243 | }
|
244 | return function (prop) {
|
245 | if (matchAll) return true
|
246 | return (
|
247 | (
|
248 | hasWild ||
|
249 | lists.exact.indexOf(prop) > -1 ||
|
250 | lists.contain.some(function (m) {
|
251 | return prop.indexOf(m) > -1
|
252 | }) ||
|
253 | lists.startWith.some(function (m) {
|
254 | return prop.indexOf(m) === 0
|
255 | }) ||
|
256 | lists.endWith.some(function (m) {
|
257 | return prop.indexOf(m) === prop.length - m.length
|
258 | })
|
259 | ) &&
|
260 | !(
|
261 | lists.notExact.indexOf(prop) > -1 ||
|
262 | lists.notContain.some(function (m) {
|
263 | return prop.indexOf(m) > -1
|
264 | }) ||
|
265 | lists.notStartWith.some(function (m) {
|
266 | return prop.indexOf(m) === 0
|
267 | }) ||
|
268 | lists.notEndWith.some(function (m) {
|
269 | return prop.indexOf(m) === prop.length - m.length
|
270 | })
|
271 | )
|
272 | )
|
273 | }
|
274 | }
|