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