Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

223 lines
5.7KB

  1. 'use strict';
  2. const ansiStyles = require('ansi-styles');
  3. const {stdout: stdoutColor, stderr: stderrColor} = require('supports-color');
  4. const {
  5. stringReplaceAll,
  6. stringEncaseCRLFWithFirstIndex
  7. } = require('./util');
  8. // `supportsColor.level` → `ansiStyles.color[name]` mapping
  9. const levelMapping = [
  10. 'ansi',
  11. 'ansi',
  12. 'ansi256',
  13. 'ansi16m'
  14. ];
  15. const styles = Object.create(null);
  16. const applyOptions = (object, options = {}) => {
  17. if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
  18. throw new Error('The `level` option should be an integer from 0 to 3');
  19. }
  20. // Detect level if not set manually
  21. const colorLevel = stdoutColor ? stdoutColor.level : 0;
  22. object.level = options.level === undefined ? colorLevel : options.level;
  23. };
  24. class ChalkClass {
  25. constructor(options) {
  26. // eslint-disable-next-line no-constructor-return
  27. return chalkFactory(options);
  28. }
  29. }
  30. const chalkFactory = options => {
  31. const chalk = {};
  32. applyOptions(chalk, options);
  33. chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_);
  34. Object.setPrototypeOf(chalk, Chalk.prototype);
  35. Object.setPrototypeOf(chalk.template, chalk);
  36. chalk.template.constructor = () => {
  37. throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.');
  38. };
  39. chalk.template.Instance = ChalkClass;
  40. return chalk.template;
  41. };
  42. function Chalk(options) {
  43. return chalkFactory(options);
  44. }
  45. for (const [styleName, style] of Object.entries(ansiStyles)) {
  46. styles[styleName] = {
  47. get() {
  48. const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty);
  49. Object.defineProperty(this, styleName, {value: builder});
  50. return builder;
  51. }
  52. };
  53. }
  54. styles.visible = {
  55. get() {
  56. const builder = createBuilder(this, this._styler, true);
  57. Object.defineProperty(this, 'visible', {value: builder});
  58. return builder;
  59. }
  60. };
  61. const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256'];
  62. for (const model of usedModels) {
  63. styles[model] = {
  64. get() {
  65. const {level} = this;
  66. return function (...arguments_) {
  67. const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
  68. return createBuilder(this, styler, this._isEmpty);
  69. };
  70. }
  71. };
  72. }
  73. for (const model of usedModels) {
  74. const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
  75. styles[bgModel] = {
  76. get() {
  77. const {level} = this;
  78. return function (...arguments_) {
  79. const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
  80. return createBuilder(this, styler, this._isEmpty);
  81. };
  82. }
  83. };
  84. }
  85. const proto = Object.defineProperties(() => {}, {
  86. ...styles,
  87. level: {
  88. enumerable: true,
  89. get() {
  90. return this._generator.level;
  91. },
  92. set(level) {
  93. this._generator.level = level;
  94. }
  95. }
  96. });
  97. const createStyler = (open, close, parent) => {
  98. let openAll;
  99. let closeAll;
  100. if (parent === undefined) {
  101. openAll = open;
  102. closeAll = close;
  103. } else {
  104. openAll = parent.openAll + open;
  105. closeAll = close + parent.closeAll;
  106. }
  107. return {
  108. open,
  109. close,
  110. openAll,
  111. closeAll,
  112. parent
  113. };
  114. };
  115. const createBuilder = (self, _styler, _isEmpty) => {
  116. const builder = (...arguments_) => {
  117. // Single argument is hot path, implicit coercion is faster than anything
  118. // eslint-disable-next-line no-implicit-coercion
  119. return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
  120. };
  121. // We alter the prototype because we must return a function, but there is
  122. // no way to create a function with a different prototype
  123. Object.setPrototypeOf(builder, proto);
  124. builder._generator = self;
  125. builder._styler = _styler;
  126. builder._isEmpty = _isEmpty;
  127. return builder;
  128. };
  129. const applyStyle = (self, string) => {
  130. if (self.level <= 0 || !string) {
  131. return self._isEmpty ? '' : string;
  132. }
  133. let styler = self._styler;
  134. if (styler === undefined) {
  135. return string;
  136. }
  137. const {openAll, closeAll} = styler;
  138. if (string.indexOf('\u001B') !== -1) {
  139. while (styler !== undefined) {
  140. // Replace any instances already present with a re-opening code
  141. // otherwise only the part of the string until said closing code
  142. // will be colored, and the rest will simply be 'plain'.
  143. string = stringReplaceAll(string, styler.close, styler.open);
  144. styler = styler.parent;
  145. }
  146. }
  147. // We can move both next actions out of loop, because remaining actions in loop won't have
  148. // any/visible effect on parts we add here. Close the styling before a linebreak and reopen
  149. // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92
  150. const lfIndex = string.indexOf('\n');
  151. if (lfIndex !== -1) {
  152. string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
  153. }
  154. return openAll + string + closeAll;
  155. };
  156. let template;
  157. const chalkTag = (chalk, ...strings) => {
  158. const [firstString] = strings;
  159. if (!Array.isArray(firstString)) {
  160. // If chalk() was called by itself or with a string,
  161. // return the string itself as a string.
  162. return strings.join(' ');
  163. }
  164. const arguments_ = strings.slice(1);
  165. const parts = [firstString.raw[0]];
  166. for (let i = 1; i < firstString.length; i++) {
  167. parts.push(
  168. String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'),
  169. String(firstString.raw[i])
  170. );
  171. }
  172. if (template === undefined) {
  173. template = require('./templates');
  174. }
  175. return template(chalk, parts.join(''));
  176. };
  177. Object.defineProperties(Chalk.prototype, styles);
  178. const chalk = Chalk(); // eslint-disable-line new-cap
  179. chalk.supportsColor = stdoutColor;
  180. chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap
  181. chalk.stderr.supportsColor = stderrColor;
  182. module.exports = chalk;