borders.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  4. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  5. var _postcss = require('postcss');
  6. var _stylehacks = require('stylehacks');
  7. var _objectAssign = require('object-assign');
  8. var _objectAssign2 = _interopRequireDefault(_objectAssign);
  9. var _clone = require('../clone');
  10. var _clone2 = _interopRequireDefault(_clone);
  11. var _genericMerge = require('../genericMerge');
  12. var _genericMerge2 = _interopRequireDefault(_genericMerge);
  13. var _insertCloned = require('../insertCloned');
  14. var _insertCloned2 = _interopRequireDefault(_insertCloned);
  15. var _parseTrbl = require('../parseTrbl');
  16. var _parseTrbl2 = _interopRequireDefault(_parseTrbl);
  17. var _hasAllProps = require('../hasAllProps');
  18. var _hasAllProps2 = _interopRequireDefault(_hasAllProps);
  19. var _getLastNode = require('../getLastNode');
  20. var _getLastNode2 = _interopRequireDefault(_getLastNode);
  21. var _getDecls = require('../getDecls');
  22. var _getDecls2 = _interopRequireDefault(_getDecls);
  23. var _getRules = require('../getRules');
  24. var _getRules2 = _interopRequireDefault(_getRules);
  25. var _getValue = require('../getValue');
  26. var _getValue2 = _interopRequireDefault(_getValue);
  27. var _minifyTrbl = require('../minifyTrbl');
  28. var _minifyTrbl2 = _interopRequireDefault(_minifyTrbl);
  29. var _canMerge = require('../canMerge');
  30. var _canMerge2 = _interopRequireDefault(_canMerge);
  31. var _colorMerge = require('../colorMerge');
  32. var _colorMerge2 = _interopRequireDefault(_colorMerge);
  33. var _remove = require('../remove');
  34. var _remove2 = _interopRequireDefault(_remove);
  35. var _trbl = require('../trbl');
  36. var _trbl2 = _interopRequireDefault(_trbl);
  37. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  38. var wsc = ['width', 'style', 'color'];
  39. var defaults = ['medium', 'none', 'currentColor'];
  40. var borderProperty = function borderProperty(property) {
  41. return 'border-' + property;
  42. };
  43. var directions = _trbl2.default.map(borderProperty);
  44. var properties = wsc.map(borderProperty);
  45. function mergeRedundant(_ref) {
  46. var values = _ref.values;
  47. var nextValues = _ref.nextValues;
  48. var decl = _ref.decl;
  49. var nextDecl = _ref.nextDecl;
  50. var index = _ref.index;
  51. var position = _ref.position;
  52. var prop = _ref.prop;
  53. var props = (0, _parseTrbl2.default)(values[position]);
  54. props[index] = nextValues[position];
  55. values.splice(position, 1);
  56. var borderValue = values.join(' ');
  57. var propertyValue = (0, _minifyTrbl2.default)(props);
  58. var origLength = (decl.value + nextDecl.prop + nextDecl.value).length;
  59. var newLength = borderValue.length + 12 + propertyValue.length;
  60. if (newLength < origLength) {
  61. decl.value = borderValue;
  62. nextDecl.prop = prop;
  63. nextDecl.value = propertyValue;
  64. }
  65. }
  66. function isCloseEnough(mapped) {
  67. return mapped[0] === mapped[1] && mapped[1] === mapped[2] || mapped[1] === mapped[2] && mapped[2] === mapped[3] || mapped[2] === mapped[3] && mapped[3] === mapped[0] || mapped[3] === mapped[0] && mapped[0] === mapped[1];
  68. }
  69. function getDistinctShorthands(mapped) {
  70. return mapped.reduce(function (a, b) {
  71. a = Array.isArray(a) ? a : [a];
  72. if (!~a.indexOf(b)) {
  73. a.push(b);
  74. }
  75. return a;
  76. });
  77. }
  78. function explode(rule) {
  79. if (rule.nodes.some(_stylehacks.detect)) {
  80. return false;
  81. }
  82. rule.walkDecls(/^border/, function (decl) {
  83. // Don't explode inherit values as they cannot be merged together
  84. if (decl.value === 'inherit') {
  85. return;
  86. }
  87. var prop = decl.prop;
  88. // border -> border-trbl
  89. if (prop === 'border') {
  90. directions.forEach(function (direction) {
  91. (0, _insertCloned2.default)(rule, decl, { prop: direction });
  92. });
  93. return decl.remove();
  94. }
  95. // border-trbl -> border-trbl-wsc
  96. if (directions.some(function (direction) {
  97. return prop === direction;
  98. })) {
  99. var _ret = function () {
  100. var values = _postcss.list.space(decl.value);
  101. wsc.forEach(function (d, i) {
  102. (0, _insertCloned2.default)(rule, decl, {
  103. prop: prop + '-' + d,
  104. value: values[i] || defaults[i]
  105. });
  106. });
  107. return {
  108. v: decl.remove()
  109. };
  110. }();
  111. if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
  112. }
  113. // border-wsc -> border-trbl-wsc
  114. wsc.some(function (style) {
  115. if (prop !== 'border-' + style) {
  116. return false;
  117. }
  118. (0, _parseTrbl2.default)(decl.value).forEach(function (value, i) {
  119. (0, _insertCloned2.default)(rule, decl, {
  120. prop: 'border-' + _trbl2.default[i] + '-' + style,
  121. value: value
  122. });
  123. });
  124. return decl.remove();
  125. });
  126. });
  127. }
  128. var borderProperties = _trbl2.default.reduce(function (props, direction) {
  129. return [].concat(props, wsc.map(function (style) {
  130. return 'border-' + direction + '-' + style;
  131. }));
  132. }, []);
  133. function merge(rule) {
  134. // Lift all inherit values from the rule, so that they don't
  135. // interfere with the merging logic.
  136. var inheritValues = (0, _getDecls2.default)(rule, borderProperties).reduce(function (values, decl) {
  137. if (decl.value === 'inherit') {
  138. decl.remove();
  139. return [].concat(values, [decl]);
  140. }
  141. return values;
  142. }, []);
  143. // border-trbl-wsc -> border-trbl
  144. _trbl2.default.forEach(function (direction) {
  145. var prop = borderProperty(direction);
  146. (0, _genericMerge2.default)({
  147. rule: rule,
  148. prop: prop,
  149. properties: wsc.map(function (style) {
  150. return prop + '-' + style;
  151. }),
  152. value: function value(rules) {
  153. return rules.map(_getValue2.default).join(' ');
  154. }
  155. });
  156. });
  157. // border-trbl-wsc -> border-wsc
  158. wsc.forEach(function (style) {
  159. var prop = borderProperty(style);
  160. if (style === 'color') {
  161. return (0, _colorMerge2.default)({
  162. rule: rule,
  163. prop: prop,
  164. properties: _trbl2.default.map(function (direction) {
  165. return 'border-' + direction + '-' + style;
  166. }),
  167. value: function value(rules) {
  168. return (0, _minifyTrbl2.default)(rules.map(_getValue2.default).join(' '));
  169. }
  170. });
  171. }
  172. return (0, _genericMerge2.default)({
  173. rule: rule,
  174. prop: prop,
  175. properties: _trbl2.default.map(function (direction) {
  176. return 'border-' + direction + '-' + style;
  177. }),
  178. value: function value(rules) {
  179. return (0, _minifyTrbl2.default)(rules.map(_getValue2.default).join(' '));
  180. },
  181. sanitize: false
  182. });
  183. });
  184. // border-trbl -> border-wsc
  185. var decls = (0, _getDecls2.default)(rule, directions);
  186. var _loop = function _loop() {
  187. var lastNode = decls[decls.length - 1];
  188. var props = decls.filter(function (node) {
  189. return node.important === lastNode.important;
  190. });
  191. var rules = (0, _getRules2.default)(props, directions);
  192. if (_hasAllProps2.default.apply(undefined, [props].concat(directions))) {
  193. wsc.forEach(function (d, i) {
  194. (0, _insertCloned2.default)(rule, lastNode, {
  195. prop: borderProperty(d),
  196. value: (0, _minifyTrbl2.default)(rules.map(function (node) {
  197. return _postcss.list.space(node.value)[i];
  198. }))
  199. });
  200. });
  201. props.forEach(_remove2.default);
  202. }
  203. decls = decls.filter(function (node) {
  204. return !~rules.indexOf(node);
  205. });
  206. };
  207. while (decls.length) {
  208. _loop();
  209. }
  210. // border-wsc -> border
  211. // border-wsc -> border + border-color
  212. // border-wsc -> border + border-dir
  213. decls = (0, _getDecls2.default)(rule, properties);
  214. var _loop2 = function _loop2() {
  215. var lastNode = decls[decls.length - 1];
  216. var props = decls.filter(function (node) {
  217. return node.important === lastNode.important;
  218. });
  219. if (_hasAllProps2.default.apply(undefined, [props].concat(properties))) {
  220. (function () {
  221. var rules = properties.map(function (prop) {
  222. return (0, _getLastNode2.default)(props, prop);
  223. });
  224. var width = rules[0];
  225. var style = rules[1];
  226. var color = rules[2];
  227. var values = rules.map(function (node) {
  228. return (0, _parseTrbl2.default)(node.value);
  229. });
  230. var mapped = [0, 1, 2, 3].map(function (i) {
  231. return [values[0][i], values[1][i], values[2][i]].join(' ');
  232. });
  233. var reduced = getDistinctShorthands(mapped);
  234. if (isCloseEnough(mapped) && _canMerge2.default.apply(undefined, rules)) {
  235. var first = mapped.indexOf(reduced[0]) !== mapped.lastIndexOf(reduced[0]);
  236. var border = (0, _insertCloned2.default)(rule, lastNode, {
  237. prop: 'border',
  238. value: first ? reduced[0] : reduced[1]
  239. });
  240. if (reduced[1]) {
  241. var value = first ? reduced[1] : reduced[0];
  242. var prop = 'border-' + _trbl2.default[mapped.indexOf(value)];
  243. rule.insertAfter(border, (0, _objectAssign2.default)((0, _clone2.default)(lastNode), {
  244. prop: prop,
  245. value: value
  246. }));
  247. }
  248. props.forEach(_remove2.default);
  249. } else if (reduced.length === 1) {
  250. rule.insertBefore(color, (0, _objectAssign2.default)((0, _clone2.default)(lastNode), {
  251. prop: 'border',
  252. value: [width, style].map(_getValue2.default).join(' ')
  253. }));
  254. props.filter(function (node) {
  255. return node.prop !== properties[2];
  256. }).forEach(_remove2.default);
  257. }
  258. })();
  259. }
  260. decls = decls.filter(function (node) {
  261. return !~props.indexOf(node);
  262. });
  263. };
  264. while (decls.length) {
  265. _loop2();
  266. }
  267. // optimize border-trbl
  268. decls = (0, _getDecls2.default)(rule, directions);
  269. var _loop3 = function _loop3() {
  270. var lastNode = decls[decls.length - 1];
  271. wsc.forEach(function (d, i) {
  272. var names = directions.filter(function (name) {
  273. return name !== lastNode.prop;
  274. }).map(function (name) {
  275. return name + '-' + d;
  276. });
  277. var props = rule.nodes.filter(function (node) {
  278. return node.prop && ~names.indexOf(node.prop) && node.important === lastNode.important;
  279. });
  280. if (_hasAllProps2.default.apply(undefined, [props].concat(names))) {
  281. var values = directions.map(function (prop) {
  282. return (0, _getLastNode2.default)(props, prop + '-' + d);
  283. }).map(function (node) {
  284. return node ? node.value : null;
  285. });
  286. var filteredValues = values.filter(Boolean);
  287. var lastNodeValue = _postcss.list.space(lastNode.value)[i];
  288. values[directions.indexOf(lastNode.prop)] = lastNodeValue;
  289. var value = (0, _minifyTrbl2.default)(values.join(' '));
  290. if (filteredValues[0] === filteredValues[1] && filteredValues[1] === filteredValues[2]) {
  291. value = filteredValues[0];
  292. }
  293. var refNode = props[props.length - 1];
  294. if (value === lastNodeValue) {
  295. refNode = lastNode;
  296. var valueArray = _postcss.list.space(lastNode.value);
  297. valueArray.splice(i, 1);
  298. lastNode.value = valueArray.join(' ');
  299. }
  300. (0, _insertCloned2.default)(rule, refNode, {
  301. prop: 'border-' + d,
  302. value: value
  303. });
  304. props.forEach(_remove2.default);
  305. }
  306. });
  307. decls = decls.filter(function (node) {
  308. return node !== lastNode;
  309. });
  310. };
  311. while (decls.length) {
  312. _loop3();
  313. }
  314. rule.walkDecls('border', function (decl) {
  315. var nextDecl = decl.next();
  316. if (!nextDecl || nextDecl.type !== 'decl') {
  317. return;
  318. }
  319. var index = directions.indexOf(nextDecl.prop);
  320. if (!~index) {
  321. return;
  322. }
  323. var values = _postcss.list.space(decl.value);
  324. var nextValues = _postcss.list.space(nextDecl.value);
  325. var config = {
  326. values: values,
  327. nextValues: nextValues,
  328. decl: decl,
  329. nextDecl: nextDecl,
  330. index: index
  331. };
  332. if (values[0] === nextValues[0] && values[2] === nextValues[2]) {
  333. return mergeRedundant(_extends({}, config, {
  334. position: 1,
  335. prop: 'border-style'
  336. }));
  337. }
  338. if (values[1] === nextValues[1] && values[2] === nextValues[2]) {
  339. return mergeRedundant(_extends({}, config, {
  340. position: 0,
  341. prop: 'border-width'
  342. }));
  343. }
  344. if (values[0] === nextValues[0] && values[1] === nextValues[1] && values[2] && nextValues[2]) {
  345. return mergeRedundant(_extends({}, config, {
  346. position: 2,
  347. prop: 'border-color'
  348. }));
  349. }
  350. });
  351. // clean-up values
  352. rule.walkDecls(/^border($|-(top|right|bottom|left))/, function (decl) {
  353. var value = [].concat(_postcss.list.space(decl.value), ['']).reduceRight(function (prev, cur, i) {
  354. if (prev === '' && cur === defaults[i]) {
  355. return prev;
  356. }
  357. return cur + ' ' + prev;
  358. }).trim() || defaults[0];
  359. decl.value = (0, _minifyTrbl2.default)(value);
  360. });
  361. // Restore inherited values
  362. inheritValues.forEach(function (decl) {
  363. return rule.append(decl);
  364. });
  365. }
  366. exports.default = {
  367. explode: explode,
  368. merge: merge
  369. };
  370. module.exports = exports['default'];