Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

stringFormatter.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. 'use strict';
  2. const _ = require('lodash');
  3. const chalk = require('chalk');
  4. const path = require('path');
  5. const stringWidth = require('string-width');
  6. const symbols = require('log-symbols');
  7. const table = require('table');
  8. const MARGIN_WIDTHS = 9;
  9. const levelColors = {
  10. info: 'blue',
  11. warning: 'yellow',
  12. error: 'red',
  13. success: undefined,
  14. };
  15. /**
  16. * @param {import('stylelint').StylelintResult[]} results
  17. * @returns {string}
  18. */
  19. function deprecationsFormatter(results) {
  20. const allDeprecationWarnings = _.flatMap(results, 'deprecations');
  21. const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, 'text');
  22. if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) {
  23. return '';
  24. }
  25. return uniqueDeprecationWarnings.reduce((output, warning) => {
  26. output += chalk.yellow('Deprecation Warning: ');
  27. output += warning.text;
  28. if (warning.reference) {
  29. output += chalk.dim(' See: ');
  30. output += chalk.dim.underline(warning.reference);
  31. }
  32. return `${output}\n`;
  33. }, '\n');
  34. }
  35. /**
  36. * @param {import('stylelint').StylelintResult[]} results
  37. * @return {string}
  38. */
  39. function invalidOptionsFormatter(results) {
  40. const allInvalidOptionWarnings = _.flatMap(results, (r) =>
  41. r.invalidOptionWarnings.map((w) => w.text),
  42. );
  43. const uniqueInvalidOptionWarnings = [...new Set(allInvalidOptionWarnings)];
  44. return uniqueInvalidOptionWarnings.reduce((output, warning) => {
  45. output += chalk.red('Invalid Option: ');
  46. output += warning;
  47. return `${output}\n`;
  48. }, '\n');
  49. }
  50. /**
  51. * @param {string} fromValue
  52. * @return {string}
  53. */
  54. function logFrom(fromValue) {
  55. if (fromValue.startsWith('<')) return fromValue;
  56. return path.relative(process.cwd(), fromValue).split(path.sep).join('/');
  57. }
  58. /**
  59. * @param {{[k: number]: number}} columnWidths
  60. * @return {number}
  61. */
  62. function getMessageWidth(columnWidths) {
  63. if (!process.stdout.isTTY) {
  64. return columnWidths[3];
  65. }
  66. const availableWidth = process.stdout.columns < 80 ? 80 : process.stdout.columns;
  67. const fullWidth = Object.values(columnWidths).reduce((a, b) => a + b);
  68. // If there is no reason to wrap the text, we won't align the last column to the right
  69. if (availableWidth > fullWidth + MARGIN_WIDTHS) {
  70. return columnWidths[3];
  71. }
  72. return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS);
  73. }
  74. /**
  75. * @param {import('stylelint').StylelintWarning[]} messages
  76. * @param {string} source
  77. * @return {string}
  78. */
  79. function formatter(messages, source) {
  80. if (!messages.length) return '';
  81. const orderedMessages = _.sortBy(
  82. messages,
  83. // eslint-disable-next-line no-confusing-arrow
  84. (m) => (m.line ? 2 : 1), // positionless first
  85. (m) => m.line,
  86. (m) => m.column,
  87. );
  88. /**
  89. * Create a list of column widths, needed to calculate
  90. * the size of the message column and if needed wrap it.
  91. * @type {{[k: string]: number}}
  92. */
  93. const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 };
  94. /**
  95. * @param {[string, string, string, string, string]} columns
  96. * @return {[string, string, string, string, string]}
  97. */
  98. function calculateWidths(columns) {
  99. for (const [key, value] of Object.entries(columns)) {
  100. const normalisedValue = value ? value.toString() : value;
  101. columnWidths[key] = Math.max(columnWidths[key], stringWidth(normalisedValue));
  102. }
  103. return columns;
  104. }
  105. let output = '\n';
  106. if (source) {
  107. output += `${chalk.underline(logFrom(source))}\n`;
  108. }
  109. const cleanedMessages = orderedMessages.map((message) => {
  110. const { line, column } = message;
  111. const severity = /** @type {keyof import('log-symbols')} */ (message.severity);
  112. /**
  113. * @type {[string, string, string, string, string]}
  114. */
  115. const row = [
  116. line ? line.toString() : '',
  117. column ? column.toString() : '',
  118. symbols[severity]
  119. ? chalk[/** @type {'blue' | 'red' | 'yellow'} */ (levelColors[severity])](symbols[severity])
  120. : severity,
  121. message.text
  122. // Remove all control characters (newline, tab and etc)
  123. .replace(/[\u0001-\u001A]+/g, ' ') // eslint-disable-line no-control-regex
  124. .replace(/\.$/, '')
  125. // eslint-disable-next-line prefer-template
  126. .replace(new RegExp(_.escapeRegExp('(' + message.rule + ')') + '$'), ''),
  127. chalk.dim(message.rule || ''),
  128. ];
  129. calculateWidths(row);
  130. return row;
  131. });
  132. output += table
  133. .table(cleanedMessages, {
  134. border: table.getBorderCharacters('void'),
  135. columns: {
  136. 0: { alignment: 'right', width: columnWidths[0], paddingRight: 0 },
  137. 1: { alignment: 'left', width: columnWidths[1] },
  138. 2: { alignment: 'center', width: columnWidths[2] },
  139. 3: {
  140. alignment: 'left',
  141. width: getMessageWidth(columnWidths),
  142. wrapWord: getMessageWidth(columnWidths) > 1,
  143. },
  144. 4: { alignment: 'left', width: columnWidths[4], paddingRight: 0 },
  145. },
  146. drawHorizontalLine: () => false,
  147. })
  148. .split('\n')
  149. .map(
  150. /**
  151. * @param {string} el
  152. * @returns {string}
  153. */
  154. (el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`)),
  155. )
  156. .join('\n');
  157. return output;
  158. }
  159. /**
  160. * @type {import('stylelint').Formatter}
  161. */
  162. module.exports = function (results) {
  163. let output = invalidOptionsFormatter(results);
  164. output += deprecationsFormatter(results);
  165. output = results.reduce((accum, result) => {
  166. // Treat parseErrors as warnings
  167. if (result.parseErrors) {
  168. result.parseErrors.forEach((error) =>
  169. result.warnings.push({
  170. line: error.line,
  171. column: error.column,
  172. rule: error.stylelintType,
  173. severity: 'error',
  174. text: `${error.text} (${error.stylelintType})`,
  175. }),
  176. );
  177. }
  178. accum += formatter(result.warnings, result.source || '');
  179. return accum;
  180. }, output);
  181. // Ensure consistent padding
  182. output = output.trim();
  183. if (output !== '') {
  184. output = `\n${output}\n\n`;
  185. }
  186. return output;
  187. };