format.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. var override = require('../utils/override');
  2. function getSystemLineBreak() {
  3. var systemLineBreak = '\n';
  4. try {
  5. var os = require('os');
  6. systemLineBreak = os.EOL;
  7. } catch (_) {
  8. // no op
  9. }
  10. return systemLineBreak;
  11. }
  12. var Breaks = {
  13. AfterAtRule: 'afterAtRule',
  14. AfterBlockBegins: 'afterBlockBegins',
  15. AfterBlockEnds: 'afterBlockEnds',
  16. AfterComment: 'afterComment',
  17. AfterProperty: 'afterProperty',
  18. AfterRuleBegins: 'afterRuleBegins',
  19. AfterRuleEnds: 'afterRuleEnds',
  20. BeforeBlockEnds: 'beforeBlockEnds',
  21. BetweenSelectors: 'betweenSelectors'
  22. };
  23. var BreakWith = {
  24. CarriageReturnLineFeed: '\r\n',
  25. LineFeed: '\n',
  26. System: getSystemLineBreak()
  27. };
  28. var IndentWith = {
  29. Space: ' ',
  30. Tab: '\t'
  31. };
  32. var Spaces = {
  33. AroundSelectorRelation: 'aroundSelectorRelation',
  34. BeforeBlockBegins: 'beforeBlockBegins',
  35. BeforeValue: 'beforeValue'
  36. };
  37. var DEFAULTS = {
  38. breaks: breaks(false),
  39. breakWith: BreakWith.System,
  40. indentBy: 0,
  41. indentWith: IndentWith.Space,
  42. spaces: spaces(false),
  43. wrapAt: false,
  44. semicolonAfterLastProperty: false
  45. };
  46. var BEAUTIFY_ALIAS = 'beautify';
  47. var KEEP_BREAKS_ALIAS = 'keep-breaks';
  48. var OPTION_SEPARATOR = ';';
  49. var OPTION_NAME_VALUE_SEPARATOR = ':';
  50. var HASH_VALUES_OPTION_SEPARATOR = ',';
  51. var HASH_VALUES_NAME_VALUE_SEPARATOR = '=';
  52. var FALSE_KEYWORD_1 = 'false';
  53. var FALSE_KEYWORD_2 = 'off';
  54. var TRUE_KEYWORD_1 = 'true';
  55. var TRUE_KEYWORD_2 = 'on';
  56. function breaks(value) {
  57. var breakOptions = {};
  58. breakOptions[Breaks.AfterAtRule] = value;
  59. breakOptions[Breaks.AfterBlockBegins] = value;
  60. breakOptions[Breaks.AfterBlockEnds] = value;
  61. breakOptions[Breaks.AfterComment] = value;
  62. breakOptions[Breaks.AfterProperty] = value;
  63. breakOptions[Breaks.AfterRuleBegins] = value;
  64. breakOptions[Breaks.AfterRuleEnds] = value;
  65. breakOptions[Breaks.BeforeBlockEnds] = value;
  66. breakOptions[Breaks.BetweenSelectors] = value;
  67. return breakOptions;
  68. }
  69. function spaces(value) {
  70. var spaceOptions = {};
  71. spaceOptions[Spaces.AroundSelectorRelation] = value;
  72. spaceOptions[Spaces.BeforeBlockBegins] = value;
  73. spaceOptions[Spaces.BeforeValue] = value;
  74. return spaceOptions;
  75. }
  76. function formatFrom(source) {
  77. if (source === undefined || source === false) {
  78. return false;
  79. }
  80. if (typeof source == 'object' && 'breakWith' in source) {
  81. source = override(source, { breakWith: mapBreakWith(source.breakWith) });
  82. }
  83. if (typeof source == 'object' && 'indentBy' in source) {
  84. source = override(source, { indentBy: parseInt(source.indentBy) });
  85. }
  86. if (typeof source == 'object' && 'indentWith' in source) {
  87. source = override(source, { indentWith: mapIndentWith(source.indentWith) });
  88. }
  89. if (typeof source == 'object') {
  90. return remapBreaks(override(DEFAULTS, source));
  91. }
  92. if (typeof source == 'string' && source == BEAUTIFY_ALIAS) {
  93. return remapBreaks(
  94. override(DEFAULTS, {
  95. breaks: breaks(true),
  96. indentBy: 2,
  97. spaces: spaces(true)
  98. })
  99. );
  100. }
  101. if (typeof source == 'string' && source == KEEP_BREAKS_ALIAS) {
  102. return remapBreaks(
  103. override(DEFAULTS, {
  104. breaks: {
  105. afterAtRule: true,
  106. afterBlockBegins: true,
  107. afterBlockEnds: true,
  108. afterComment: true,
  109. afterRuleEnds: true,
  110. beforeBlockEnds: true
  111. }
  112. })
  113. );
  114. }
  115. if (typeof source == 'string') {
  116. return remapBreaks(override(DEFAULTS, toHash(source)));
  117. }
  118. return DEFAULTS;
  119. }
  120. function toHash(string) {
  121. return string
  122. .split(OPTION_SEPARATOR)
  123. .reduce(function(accumulator, directive) {
  124. var parts = directive.split(OPTION_NAME_VALUE_SEPARATOR);
  125. var name = parts[0];
  126. var value = parts[1];
  127. if (name == 'breaks' || name == 'spaces') {
  128. accumulator[name] = hashValuesToHash(value);
  129. } else if (name == 'indentBy' || name == 'wrapAt') {
  130. accumulator[name] = parseInt(value);
  131. } else if (name == 'indentWith') {
  132. accumulator[name] = mapIndentWith(value);
  133. } else if (name == 'breakWith') {
  134. accumulator[name] = mapBreakWith(value);
  135. }
  136. return accumulator;
  137. }, {});
  138. }
  139. function hashValuesToHash(string) {
  140. return string
  141. .split(HASH_VALUES_OPTION_SEPARATOR)
  142. .reduce(function(accumulator, directive) {
  143. var parts = directive.split(HASH_VALUES_NAME_VALUE_SEPARATOR);
  144. var name = parts[0];
  145. var value = parts[1];
  146. accumulator[name] = normalizeValue(value);
  147. return accumulator;
  148. }, {});
  149. }
  150. function normalizeValue(value) {
  151. switch (value) {
  152. case FALSE_KEYWORD_1:
  153. case FALSE_KEYWORD_2:
  154. return false;
  155. case TRUE_KEYWORD_1:
  156. case TRUE_KEYWORD_2:
  157. return true;
  158. default:
  159. return value;
  160. }
  161. }
  162. function mapBreakWith(value) {
  163. switch (value) {
  164. case 'windows':
  165. case 'crlf':
  166. case BreakWith.CarriageReturnLineFeed:
  167. return BreakWith.CarriageReturnLineFeed;
  168. case 'unix':
  169. case 'lf':
  170. case BreakWith.LineFeed:
  171. return BreakWith.LineFeed;
  172. default:
  173. return BreakWith.System;
  174. }
  175. }
  176. function mapIndentWith(value) {
  177. switch (value) {
  178. case 'space':
  179. return IndentWith.Space;
  180. case 'tab':
  181. return IndentWith.Tab;
  182. default:
  183. return value;
  184. }
  185. }
  186. function remapBreaks(source) {
  187. for (var key in Breaks) {
  188. var breakName = Breaks[key];
  189. var breakValue = source.breaks[breakName];
  190. if (breakValue === true) {
  191. source.breaks[breakName] = source.breakWith;
  192. } else if (breakValue === false) {
  193. source.breaks[breakName] = '';
  194. } else {
  195. source.breaks[breakName] = source.breakWith.repeat(parseInt(breakValue));
  196. }
  197. }
  198. return source;
  199. }
  200. module.exports = {
  201. Breaks: Breaks,
  202. Spaces: Spaces,
  203. formatFrom: formatFrom
  204. };