format.js 5.4 KB

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