index.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. const pxRegex = require("./lib/pixel-unit-regex");
  2. const filterPropList = require("./lib/filter-prop-list");
  3. const type = require("./lib/type");
  4. const defaults = {
  5. // rootValue: 16,
  6. unitPrecision: 5,
  7. unit: "rpx",
  8. selectorBlackList: [],
  9. propBlackList: [],
  10. replace: true,
  11. mediaQuery: false,
  12. minPixelValue: 0,
  13. exclude: null,
  14. // 默认设计稿按照750宽,2倍图的出
  15. transform: x => 2 * x
  16. };
  17. module.exports = options => {
  18. const opts = Object.assign({}, defaults, options);
  19. const unsatisfyPropList = createPropListMatcher(opts.propBlackList);
  20. const exclude = opts.exclude;
  21. const pxReplaceFunc = createPxReplace(
  22. opts.unit,
  23. opts.unitPrecision,
  24. opts.minPixelValue,
  25. opts.transform
  26. );
  27. // use flag to exclude process for specific file.
  28. let isExcludeFile = false;
  29. return {
  30. postcssPlugin: "postcss-pxtorpx-pro",
  31. Once(css) {
  32. const filePath = css.source.input.file;
  33. if (
  34. exclude &&
  35. ((type.isFunction(exclude) && exclude(filePath)) ||
  36. (type.isString(exclude) && filePath.indexOf(exclude) !== -1) ||
  37. (type.isRegExp(exclude) && filePath.match(exclude) !== null))
  38. ) {
  39. isExcludeFile = true;
  40. return;
  41. } else {
  42. isExcludeFile = false;
  43. }
  44. },
  45. Declaration(decl) {
  46. if (isExcludeFile) return;
  47. if (
  48. decl.value.indexOf("px") === -1 ||
  49. unsatisfyPropList(decl.prop) ||
  50. blacklistedSelector(opts.selectorBlackList, decl.parent.selector)
  51. )
  52. return;
  53. const value = decl.value.replace(pxRegex, pxReplaceFunc);
  54. // if rem unit already exists, do not add or replace
  55. if (declarationExists(decl.parent, decl.prop, value)) return;
  56. if (opts.replace) {
  57. decl.value = value;
  58. } else {
  59. decl.cloneAfter({ value: value });
  60. }
  61. },
  62. AtRule(atRule) {
  63. if (isExcludeFile) return;
  64. if (opts.mediaQuery && atRule.name === "media") {
  65. if (atRule.params.indexOf("px") === -1) return;
  66. atRule.params = atRule.params.replace(pxRegex, pxReplaceFunc);
  67. }
  68. }
  69. };
  70. };
  71. module.exports.postcss = true;
  72. function createPxReplace(unit, unitPrecision, minPixelValue, transform) {
  73. return (m, $1) => {
  74. if (!$1) return m;
  75. const pixels = parseFloat($1);
  76. if (pixels < minPixelValue) return m;
  77. const fixedVal = toFixed(transform(pixels), unitPrecision);
  78. return fixedVal === 0 ? "0" : fixedVal + unit;
  79. };
  80. }
  81. function toFixed(number, precision) {
  82. const multiplier = Math.pow(10, precision + 1),
  83. wholeNumber = Math.floor(number * multiplier);
  84. return (Math.round(wholeNumber / 10) * 10) / multiplier;
  85. }
  86. function declarationExists(decls, prop, value) {
  87. return decls.some(decl => decl.prop === prop && decl.value === value);
  88. }
  89. function blacklistedSelector(blacklist, selector) {
  90. if (typeof selector !== "string") return;
  91. return blacklist.some(regex => {
  92. if (typeof regex === "string") {
  93. return selector.indexOf(regex) !== -1;
  94. }
  95. return selector.match(regex);
  96. });
  97. }
  98. function createPropListMatcher(propList) {
  99. const hasWild = propList.indexOf("*") > -1;
  100. const matchAll = hasWild && propList.length === 1;
  101. const lists = {
  102. exact: filterPropList.exact(propList),
  103. contain: filterPropList.contain(propList),
  104. startWith: filterPropList.startWith(propList),
  105. endWith: filterPropList.endWith(propList),
  106. notExact: filterPropList.notExact(propList),
  107. notContain: filterPropList.notContain(propList),
  108. notStartWith: filterPropList.notStartWith(propList),
  109. notEndWith: filterPropList.notEndWith(propList)
  110. };
  111. return prop => {
  112. if (matchAll) return true;
  113. return (
  114. (hasWild ||
  115. lists.exact.indexOf(prop) > -1 ||
  116. lists.contain.some(function(m) {
  117. return prop.indexOf(m) > -1;
  118. }) ||
  119. lists.startWith.some(function(m) {
  120. return prop.indexOf(m) === 0;
  121. }) ||
  122. lists.endWith.some(function(m) {
  123. return prop.indexOf(m) === prop.length - m.length;
  124. })) &&
  125. !(
  126. lists.notExact.indexOf(prop) > -1 ||
  127. lists.notContain.some(function(m) {
  128. return prop.indexOf(m) > -1;
  129. }) ||
  130. lists.notStartWith.some(function(m) {
  131. return prop.indexOf(m) === 0;
  132. }) ||
  133. lists.notEndWith.some(function(m) {
  134. return prop.indexOf(m) === prop.length - m.length;
  135. })
  136. )
  137. );
  138. };
  139. }