From 1fb6daf30d2159740a6f9ccaf6daa5cf7a0cff37 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 17:51:29 +0300 Subject: [PATCH 01/12] wider benchmark --- benchmark.js | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/benchmark.js b/benchmark.js index f4e9cf3..dc24696 100644 --- a/benchmark.js +++ b/benchmark.js @@ -3,22 +3,48 @@ const chalk = require('.'); suite('chalk', () => { - set('iterations', 100000); + set('iterations', 1000000); - bench('single style', () => { + const chalkRed = chalk.red; + const chalkBgRed = chalk.bgRed; + const chalkBlueBgRed = chalk.blue.bgRed; + const chalkBlueBgRedBold = chalk.blue.bgRed.bold; + + const blueStyledString = 'the fox jumps' + chalk.blue('over the lazy dog') + '!'; + + bench('1 style', () => { chalk.red('the fox jumps over the lazy dog'); }); - bench('several styles', () => { + bench('2 styles', () => { + chalk.blue.bgRed('the fox jumps over the lazy dog'); + }); + + bench('3 styles', () => { chalk.blue.bgRed.bold('the fox jumps over the lazy dog'); }); - const cached = chalk.blue.bgRed.bold; - bench('cached styles', () => { - cached('the fox jumps over the lazy dog'); + bench('cached: 1 style', () => { + chalkRed('the fox jumps over the lazy dog'); + }); + + bench('cached: 2 styles', () => { + chalkBlueBgRed('the fox jumps over the lazy dog'); + }); + + bench('cached: 3 styles', () => { + chalkBlueBgRedBold('the fox jumps over the lazy dog'); + }); + + bench('cached: 1 style with newline', () => { + chalkRed('the fox jumps\nover the lazy dog'); + }); + + bench('cached: 1 style nested intersecting', () => { + chalkRed(blueStyledString); }); - bench('nested styles', () => { - chalk.red('the fox jumps', chalk.underline.bgBlue('over the lazy dog') + '!'); + bench('cached: 1 style nested non-intersecting', () => { + chalkBgRed(blueStyledString); }); }); From b251c00ae3eaa49161162279b270194b33a90e57 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 18:03:32 +0300 Subject: [PATCH 02/12] improved replaces, builder instantiation --- index.js | 78 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 9f5575a..51fc535 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,44 @@ const levelMapping = [ 'ansi16m' ]; +const stringReplaceAll = (str, substr, replacer) => { + let idx = str.indexOf(substr); + if (idx === -1) { + return str; + } + + const subLen = substr.length; + let end = 0; + let res = ''; + do { + res += str.substr(end, idx - end) + replacer; + end = idx + subLen; + idx = str.indexOf(substr, end); + } while (idx !== -1); + + res += str.substr(end); + return res; +}; + +const stringEncaseCRLF = (str, prefix, postfix) => { + let idx = str.indexOf('\n'); + if (idx === -1) { + return str; + } + + let end = 0; + let res = ''; + do { + const gotCR = str[idx - 1] === '\r'; + res += str.substr(end, (gotCR ? idx - 1 : idx) - end) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + end = idx + 1; + idx = str.indexOf('\n', end); + } while (idx !== -1); + + res += str.substr(end); + return res; +}; + // `color-convert` models to exclude from the Chalk API due to conflicts and such const skipModels = new Set(['gray']); @@ -117,37 +155,39 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) { }; } -const proto = Object.defineProperties(() => {}, styles); - -const createBuilder = (self, _styles, _isEmpty) => { - const builder = (...arguments_) => applyStyle(builder, ...arguments_); - builder._styles = _styles; - builder._isEmpty = _isEmpty; - - Object.defineProperty(builder, 'level', { +const proto = Object.defineProperties(() => {}, { + ...styles, + level: { enumerable: true, get() { - return self.level; + return this._generator.level; }, set(level) { - self.level = level; + this._generator.level = level; } - }); - - Object.defineProperty(builder, 'enabled', { + }, + enabled: { enumerable: true, get() { - return self.enabled; + return this._generator.enabled; }, set(enabled) { - self.enabled = enabled; + this._generator.enabled = enabled; } - }); + } +}); + +const createBuilder = (self, _styles, _isEmpty) => { + const builder = (...arguments_) => applyStyle(builder, ...arguments_); // `__proto__` is used because we must return a function, but there is // no way to create a function with a different prototype builder.__proto__ = proto; // eslint-disable-line no-proto + builder._generator = self; + builder._styles = _styles; + builder._isEmpty = _isEmpty; + return builder; }; @@ -162,12 +202,14 @@ const applyStyle = (self, ...arguments_) => { // Replace any instances already present with a re-opening code // otherwise only the part of the string until said closing code // will be colored, and the rest will simply be 'plain'. - string = code.open + string.replace(code.closeRe, code.open) + code.close; + string = stringReplaceAll(string, code.close, code.open); // Close the styling before a linebreak and reopen // after next line to fix a bleed issue on macOS // https://github.com/chalk/chalk/pull/92 - string = string.replace(/\r?\n/g, `${code.close}$&${code.open}`); + string = stringEncaseCRLF(string, code.close, code.open); + + string = code.open + string + code.close; } return string; From 091e8ed9362ee42ce391de6916ddb5c17246bc44 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 18:13:31 +0300 Subject: [PATCH 03/12] optimized arguments handling, moved out of hot zone --- index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 51fc535..dd7cbf1 100644 --- a/index.js +++ b/index.js @@ -178,7 +178,10 @@ const proto = Object.defineProperties(() => {}, { }); const createBuilder = (self, _styles, _isEmpty) => { - const builder = (...arguments_) => applyStyle(builder, ...arguments_); + const builder = (...arguments_) => { + // eslint-disable-next-line no-implicit-coercion + return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); + }; // `__proto__` is used because we must return a function, but there is // no way to create a function with a different prototype @@ -191,9 +194,7 @@ const createBuilder = (self, _styles, _isEmpty) => { return builder; }; -const applyStyle = (self, ...arguments_) => { - let string = arguments_.join(' '); - +const applyStyle = (self, string) => { if (!self.enabled || self.level <= 0 || !string) { return self._isEmpty ? '' : string; } From 42d45ba75a9ebc43abb0ff1e14b40cadb8f24c9e Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 18:20:38 +0300 Subject: [PATCH 04/12] removed all regexp stuff --- index.js | 11 ++--------- package.json | 1 - 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index dd7cbf1..97ade4f 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,4 @@ 'use strict'; -const escapeStringRegexp = require('escape-string-regexp'); const ansiStyles = require('ansi-styles'); const {stdout: stdoutColor} = require('supports-color'); const template = require('./templates.js'); @@ -95,8 +94,6 @@ function Chalk(options) { } for (const [styleName, style] of Object.entries(ansiStyles)) { - style.closeRe = new RegExp(escapeStringRegexp(style.close), 'g'); - styles[styleName] = { get() { return createBuilder(this, [...(this._styles || []), style], this._isEmpty); @@ -110,7 +107,6 @@ styles.visible = { } }; -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); for (const model of Object.keys(ansiStyles.color.ansi)) { if (skipModels.has(model)) { continue; @@ -123,8 +119,7 @@ for (const model of Object.keys(ansiStyles.color.ansi)) { const open = ansiStyles.color[levelMapping[level]][model](...arguments_); const codes = { open, - close: ansiStyles.color.close, - closeRe: ansiStyles.color.closeRe + close: ansiStyles.color.close }; return createBuilder(this, [...(this._styles || []), codes], this._isEmpty); }; @@ -132,7 +127,6 @@ for (const model of Object.keys(ansiStyles.color.ansi)) { }; } -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); for (const model of Object.keys(ansiStyles.bgColor.ansi)) { if (skipModels.has(model)) { continue; @@ -146,8 +140,7 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) { const open = ansiStyles.bgColor[levelMapping[level]][model](...arguments_); const codes = { open, - close: ansiStyles.bgColor.close, - closeRe: ansiStyles.bgColor.closeRe + close: ansiStyles.bgColor.close }; return createBuilder(this, [...(this._styles || []), codes], this._isEmpty); }; diff --git a/package.json b/package.json index b18d8a3..48712eb 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ ], "dependencies": { "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", "supports-color": "^6.1.0" }, "devDependencies": { From efab07c0934069b813c0cda2dd719e019cd7082d Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 18:33:09 +0300 Subject: [PATCH 05/12] replaced arrays by linked lists --- index.js | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 97ade4f..c5bde92 100644 --- a/index.js +++ b/index.js @@ -96,14 +96,14 @@ function Chalk(options) { for (const [styleName, style] of Object.entries(ansiStyles)) { styles[styleName] = { get() { - return createBuilder(this, [...(this._styles || []), style], this._isEmpty); + return createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); } }; } styles.visible = { get() { - return createBuilder(this, this._styles || [], true); + return createBuilder(this, this._styler, true); } }; @@ -116,12 +116,8 @@ for (const model of Object.keys(ansiStyles.color.ansi)) { get() { const {level} = this; return function (...arguments_) { - const open = ansiStyles.color[levelMapping[level]][model](...arguments_); - const codes = { - open, - close: ansiStyles.color.close - }; - return createBuilder(this, [...(this._styles || []), codes], this._isEmpty); + const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler); + return createBuilder(this, styler, this._isEmpty); }; } }; @@ -137,12 +133,8 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) { get() { const {level} = this; return function (...arguments_) { - const open = ansiStyles.bgColor[levelMapping[level]][model](...arguments_); - const codes = { - open, - close: ansiStyles.bgColor.close - }; - return createBuilder(this, [...(this._styles || []), codes], this._isEmpty); + const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler); + return createBuilder(this, styler, this._isEmpty); }; } }; @@ -170,7 +162,15 @@ const proto = Object.defineProperties(() => {}, { } }); -const createBuilder = (self, _styles, _isEmpty) => { +const createStyler = (open, close, parent) => { + return { + open, + close, + parent + }; +}; + +const createBuilder = (self, _styler, _isEmpty) => { const builder = (...arguments_) => { // eslint-disable-next-line no-implicit-coercion return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); @@ -181,7 +181,7 @@ const createBuilder = (self, _styles, _isEmpty) => { builder.__proto__ = proto; // eslint-disable-line no-proto builder._generator = self; - builder._styles = _styles; + builder._styler = _styler; builder._isEmpty = _isEmpty; return builder; @@ -192,18 +192,20 @@ const applyStyle = (self, string) => { return self._isEmpty ? '' : string; } - for (const code of self._styles.slice().reverse()) { + let styler = self._styler; + while (styler !== undefined) { // Replace any instances already present with a re-opening code // otherwise only the part of the string until said closing code // will be colored, and the rest will simply be 'plain'. - string = stringReplaceAll(string, code.close, code.open); + string = stringReplaceAll(string, styler.close, styler.open); // Close the styling before a linebreak and reopen // after next line to fix a bleed issue on macOS // https://github.com/chalk/chalk/pull/92 - string = stringEncaseCRLF(string, code.close, code.open); + string = stringEncaseCRLF(string, styler.close, styler.open); - string = code.open + string + code.close; + string = styler.open + string + styler.close; + styler = styler.parent; } return string; From fb5da89bcbcbfd10d95db1916055f29aa8ff7c7b Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Tue, 26 Mar 2019 19:09:35 +0300 Subject: [PATCH 06/12] relaxed loops --- index.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index c5bde92..6ce5bcb 100644 --- a/index.js +++ b/index.js @@ -193,19 +193,27 @@ const applyStyle = (self, string) => { } let styler = self._styler; - while (styler !== undefined) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - string = stringReplaceAll(string, styler.close, styler.open); + if (styler !== undefined) { + let closeAll = ''; + let openAll = ''; + while (styler !== undefined) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + string = stringReplaceAll(string, styler.close, styler.open); + + openAll = styler.open + openAll; + closeAll += styler.close; + styler = styler.parent; + } + + // We can move both next actions out of loop, because remaining actions in loop won't have any/visible effect on parts we add here // Close the styling before a linebreak and reopen // after next line to fix a bleed issue on macOS // https://github.com/chalk/chalk/pull/92 - string = stringEncaseCRLF(string, styler.close, styler.open); - - string = styler.open + string + styler.close; - styler = styler.parent; + string = stringEncaseCRLF(string, closeAll, openAll); + string = openAll + string + closeAll; } return string; From 200373790f3fc6482f34d5f64fb809acdec0e9d6 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Wed, 27 Mar 2019 08:24:03 +0300 Subject: [PATCH 07/12] precomputed codes, fastpath for no-lf/no-escape strings --- index.js | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 6ce5bcb..b69a052 100644 --- a/index.js +++ b/index.js @@ -30,12 +30,7 @@ const stringReplaceAll = (str, substr, replacer) => { return res; }; -const stringEncaseCRLF = (str, prefix, postfix) => { - let idx = str.indexOf('\n'); - if (idx === -1) { - return str; - } - +const stringEncaseCRLFWithFirstIndex = (str, prefix, postfix, idx) => { let end = 0; let res = ''; do { @@ -163,9 +158,21 @@ const proto = Object.defineProperties(() => {}, { }); const createStyler = (open, close, parent) => { + let openAll; + let closeAll; + if (parent === undefined) { + openAll = open; + closeAll = close; + } else { + openAll = parent.openAll + open; + closeAll = close + parent.closeAll; + } + return { open, close, + openAll, + closeAll, parent }; }; @@ -194,29 +201,32 @@ const applyStyle = (self, string) => { let styler = self._styler; - if (styler !== undefined) { - let closeAll = ''; - let openAll = ''; + if (styler === undefined) { + return string; + } + + const {openAll, closeAll} = styler; + if (string.indexOf('\u001B') !== -1) { while (styler !== undefined) { // Replace any instances already present with a re-opening code // otherwise only the part of the string until said closing code // will be colored, and the rest will simply be 'plain'. string = stringReplaceAll(string, styler.close, styler.open); - openAll = styler.open + openAll; - closeAll += styler.close; styler = styler.parent; } + } - // We can move both next actions out of loop, because remaining actions in loop won't have any/visible effect on parts we add here - // Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS - // https://github.com/chalk/chalk/pull/92 - string = stringEncaseCRLF(string, closeAll, openAll); - string = openAll + string + closeAll; + // We can move both next actions out of loop, because remaining actions in loop won't have any/visible effect on parts we add here + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + const lfIdx = string.indexOf('\n'); + if (lfIdx !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIdx); } - return string; + return openAll + string + closeAll; }; const chalkTag = (chalk, ...strings) => { From cbc9f160e8826b179fc9614d01528e7e742482a7 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Wed, 27 Mar 2019 08:37:25 +0300 Subject: [PATCH 08/12] caching simple getters --- index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index b69a052..43373fc 100644 --- a/index.js +++ b/index.js @@ -91,14 +91,18 @@ function Chalk(options) { for (const [styleName, style] of Object.entries(ansiStyles)) { styles[styleName] = { get() { - return createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); + const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); + Object.defineProperty(this, styleName, {value: builder}); + return builder; } }; } styles.visible = { get() { - return createBuilder(this, this._styler, true); + const builder = createBuilder(this, this._styler, true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; } }; From 5f0835a0aa5524b2831d9bbc9882ad561ab83265 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Wed, 27 Mar 2019 09:25:16 +0300 Subject: [PATCH 09/12] added test for crlf --- test/chalk.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/chalk.js b/test/chalk.js index 240f0f9..3f90ba6 100644 --- a/test/chalk.js +++ b/test/chalk.js @@ -82,6 +82,10 @@ test('line breaks should open and close colors', t => { t.is(chalk.grey('hello\nworld'), '\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m'); }); +test('line breaks should open and close colors with CRLF', t => { + t.is(chalk.grey('hello\r\nworld'), '\u001B[90mhello\u001B[39m\r\n\u001B[90mworld\u001B[39m'); +}); + test('properly convert RGB to 16 colors on basic color terminals', t => { t.is(new chalk.Instance({level: 1}).hex('#FF0000')('hello'), '\u001B[91mhello\u001B[39m'); t.is(new chalk.Instance({level: 1}).bgHex('#FF0000')('hello'), '\u001B[101mhello\u001B[49m'); From f58b05c39905d1787a99ef21a05c3a39555386e6 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Wed, 27 Mar 2019 09:46:22 +0300 Subject: [PATCH 10/12] comment --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 43373fc..d324263 100644 --- a/index.js +++ b/index.js @@ -183,6 +183,7 @@ const createStyler = (open, close, parent) => { const createBuilder = (self, _styler, _isEmpty) => { const builder = (...arguments_) => { + // Single argument is hot path, implicit coercion is faster than anything // eslint-disable-next-line no-implicit-coercion return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); }; From e63d61d063cf77031b31536794078cf452d96f23 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Sat, 29 Jun 2019 13:40:13 +0300 Subject: [PATCH 11/12] style --- index.js | 44 ++++++++------------------------------------ lib/util.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 lib/util.js diff --git a/index.js b/index.js index d324263..21d3a83 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,11 @@ const ansiStyles = require('ansi-styles'); const {stdout: stdoutColor} = require('supports-color'); const template = require('./templates.js'); +const { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +} = require('./lib/util'); + // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ 'ansi', @@ -11,39 +16,6 @@ const levelMapping = [ 'ansi16m' ]; -const stringReplaceAll = (str, substr, replacer) => { - let idx = str.indexOf(substr); - if (idx === -1) { - return str; - } - - const subLen = substr.length; - let end = 0; - let res = ''; - do { - res += str.substr(end, idx - end) + replacer; - end = idx + subLen; - idx = str.indexOf(substr, end); - } while (idx !== -1); - - res += str.substr(end); - return res; -}; - -const stringEncaseCRLFWithFirstIndex = (str, prefix, postfix, idx) => { - let end = 0; - let res = ''; - do { - const gotCR = str[idx - 1] === '\r'; - res += str.substr(end, (gotCR ? idx - 1 : idx) - end) + prefix + (gotCR ? '\r\n' : '\n') + postfix; - end = idx + 1; - idx = str.indexOf('\n', end); - } while (idx !== -1); - - res += str.substr(end); - return res; -}; - // `color-convert` models to exclude from the Chalk API due to conflicts and such const skipModels = new Set(['gray']); @@ -226,9 +198,9 @@ const applyStyle = (self, string) => { // Close the styling before a linebreak and reopen // after next line to fix a bleed issue on macOS // https://github.com/chalk/chalk/pull/92 - const lfIdx = string.indexOf('\n'); - if (lfIdx !== -1) { - string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIdx); + const lfIndex = string.indexOf('\n'); + if (lfIndex !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; diff --git a/lib/util.js b/lib/util.js new file mode 100644 index 0000000..8ce1166 --- /dev/null +++ b/lib/util.js @@ -0,0 +1,37 @@ +const stringReplaceAll = (string, substring, replacer) => { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } + + const subLen = substring.length; + let end = 0; + let res = ''; + do { + res += string.substr(end, index - end) + replacer; + end = index + subLen; + index = string.indexOf(substring, end); + } while (index !== -1); + + res += string.substr(end); + return res; +}; + +const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { + let end = 0; + let res = ''; + do { + const gotCR = string[index - 1] === '\r'; + res += string.substr(end, (gotCR ? index - 1 : index) - end) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + end = index + 1; + index = string.indexOf('\n', end); + } while (index !== -1); + + res += string.substr(end); + return res; +}; + +module.exports = { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +}; From 0c671f5ba660742980692fddc64047316b661303 Mon Sep 17 00:00:00 2001 From: Yanis Benson Date: Sat, 29 Jun 2019 13:44:24 +0300 Subject: [PATCH 12/12] fixed examples/screenshot.js style --- examples/screenshot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/screenshot.js b/examples/screenshot.js index 7d195a6..37f5850 100644 --- a/examples/screenshot.js +++ b/examples/screenshot.js @@ -1,6 +1,6 @@ 'use strict'; -const chalk = require('..'); const styles = require('ansi-styles'); +const chalk = require('..'); // Generates screenshot for (const key of Object.keys(styles)) {