Repository: chalk/chalk (js)

Issue:
Improve require speed
<!-- Issuehunt Badges -->
[<img alt="Issuehunt badges" src="https://img.shields.io/badge/IssueHunt-%2460%20Funded-%2300A156.svg" />](https://issuehunt.io/r/chalk/chalk/issues/180)
<!-- /Issuehunt Badges -->


I tested this by creating a new project and adding just [time-require](https://www.npmjs.com/package/time-require) and chalk.  I then created a simple `time.js` file with the following contents:

```js
require('time-require')
require('chalk')
```

**Results for Chalk 1.x (1.1.3 as of this posting):**
```
Start time: (2017-07-22 16:50:03 UTC) [treshold=1%]
#  module                                                                time  %
1  pretty-ms (node_modules/pretty-ms/index.js)                            1ms  ▇▇▇▇▇▇▇▇ 11%
2  ansi-styles (node_modules/time-re...ules/ansi-styles/ansi-styles.js)   1ms  ▇▇▇▇▇▇▇▇ 11%
3  has-color (node_modules/has-color/index.js)                            1ms  ▇▇▇▇▇▇▇▇ 11%
4  chalk (node_modules/time-require/node_modules/chalk/index.js)          3ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 33%
5  escape-string-regexp (node_modules/escape-string-regexp/index.js)      1ms  ▇▇▇▇▇▇▇▇ 11%
6  ansi-styles (node_modules/ansi-styles/index.js)                        1ms  ▇▇▇▇▇▇▇▇ 11%
7  has-ansi (node_modules/has-ansi/index.js)                              1ms  ▇▇▇▇▇▇▇▇ 11%
8  chalk (node_modules/chalk/index.js)                                    3ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 33%
Total require(): 16
Total time: 9ms
```

**Results for Chalk 2.x (2.0.1 as of this posting):**
```Start time: (2017-07-22 16:48:22 UTC) [treshold=1%]
 #  module                                                                time  %
 1  text-table (node_modules/text-table/index.js)                          1ms  ▇▇▇▇▇ 6%
 2  date-time (node_modules/date-time/index.js)                            1ms  ▇▇▇▇▇ 6%
 3  parse-ms (node_modules/parse-ms/index.js)                              1ms  ▇▇▇▇▇ 6%
 4  pretty-ms (node_modules/pretty-ms/index.js)                            1ms  ▇▇▇▇▇ 6%
 5  ansi-styles (node_modules/time-re...ules/ansi-styles/ansi-styles.js)   1ms  ▇▇▇▇▇ 6%
 6  has-color (node_modules/has-color/index.js)                            1ms  ▇▇▇▇▇ 6%
 7  chalk (node_modules/time-require/node_modules/chalk/index.js)          2ms  ▇▇▇▇▇▇▇▇▇ 13%
 8  escape-string-regexp (node_modules/escape-string-regexp/index.js)      1ms  ▇▇▇▇▇ 6%
 9  ./conversions (node_modules/color-convert/conversions.js)              2ms  ▇▇▇▇▇▇▇▇▇ 13%
10  color-convert (node_modules/color-convert/index.js)                    5ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 31%
11  ansi-styles (node_modules/ansi-styles/index.js)                        6ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 38%
12  os (os)                                                                1ms  ▇▇▇▇▇ 6%
13  supports-color (node_modules/supports-color/index.js)                  1ms  ▇▇▇▇▇ 6%
14  ./templates.js (node_modules/chalk/templates.js)                       1ms  ▇▇▇▇▇ 6%
15  chalk (node_modules/chalk/index.js)                                   10ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 63%
Total require(): 20
Total time: 16ms
```

I ran the tests several times locally and the results were all within 1ms each time.

For long running processes, the difference here is obviously negligible so I'll understand if this gets closed out.

That being said, for CLI utils where boot time and responsiveness are far more important, it's unfortunate that just using 2.x adds an additional 5ms+ on average that you can't get back.


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>


### Backers (Total: $60.00)

- [<img src='https://avatars3.githubusercontent.com/u/44827199?v=4' alt='issuehunt' width=24 height=24> issuehunt](https://issuehunt.io/u/issuehunt) ($60.00)
### Submitted pull Requests
- [#358 Improve require speed](https://issuehunt.io/r/chalk/chalk/pull/358)
---

#### [Become a backer now!](https://issuehunt.io/r/chalk/chalk/issues/180)
#### [Or submit a pull request to get the deposits!](https://issuehunt.io/r/chalk/chalk/issues/180)
### Tips

- Checkout the [Issuehunt explorer](https://issuehunt.io/r/chalk/chalk/) to discover more funded issues.
- Need some help from other developers? [Add your repositories](https://issuehunt.io/r/new) on IssueHunt to raise funds.
---
IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->

Pull request description:
Improve require speed
- Lazy load templates(runtime overhead should be zero due to prototype check overlapping with `=== undefined` check)
 - Don't access `ansi-styles` color space converters unless needed

This patch works in conjunction with(but works without them too, though to much smaller effect):
 - https://github.com/chalk/ansi-styles/pull/53
 - https://github.com/chalk/supports-color/pull/101

Require speed test results before:
![before](https://user-images.githubusercontent.com/176214/61600033-16f75f00-ac37-11e9-8635-bb178a266fcf.png)

Require speed test results after(pure require, without using template nor color conversion functionality):
![after](https://user-images.githubusercontent.com/176214/61600036-1959b900-ac37-11e9-9dff-27554ca2a899.png)

Note: in both cases, a huge part of time is spent initializing `tty`(or initializing `process.stdout` and `process.stderr`, which includes initializing `tty`), which is also triggered by a lot of other parts of stdlib(for example by `console.log` with non-string arguments), and is required in order to detect if output supports color, so is expected to be initialized anyway by a lot of real life programs(notable exception: programs which conditionally produce no output). If we subtract the time required for this initializations, the final numbers will be ~10ms and ~5ms, respectively.

Fixes #180


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>

### Referenced issues

This pull request has been submitted to:
- [#180: Improve require speed](https://issuehunt.io/repos/11855195/issues/180)
---

IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->

Patch:
diff --git a/source/index.js b/source/index.js
index 686758b..53fcc16 100644
--- a/source/index.js
+++ b/source/index.js
@@ -1,7 +1,6 @@
 'use strict';
 const ansiStyles = require('ansi-styles');
 const {stdout: stdoutColor, stderr: stderrColor} = require('supports-color');
-const template = require('./templates');
 const {
 	stringReplaceAll,
 	stringEncaseCRLFWithFirstIndex
@@ -15,9 +14,6 @@ const levelMapping = [
 	'ansi16m'
 ];
 
-// `color-convert` models to exclude from the Chalk API due to conflicts and such
-const skipModels = new Set(['gray']);
-
 const styles = Object.create(null);
 
 const applyOptions = (object, options = {}) => {
@@ -76,11 +72,9 @@ styles.visible = {
 	}
 };
 
-for (const model of Object.keys(ansiStyles.color.ansi)) {
-	if (skipModels.has(model)) {
-		continue;
-	}
+const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256'];
 
+for (const model of usedModels) {
 	styles[model] = {
 		get() {
 			const {level} = this;
@@ -92,11 +86,7 @@ for (const model of Object.keys(ansiStyles.color.ansi)) {
 	};
 }
 
-for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
-	if (skipModels.has(model)) {
-		continue;
-	}
-
+for (const model of usedModels) {
 	const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
 	styles[bgModel] = {
 		get() {
@@ -194,6 +184,7 @@ const applyStyle = (self, string) => {
 	return openAll + string + closeAll;
 };
 
+let template;
 const chalkTag = (chalk, ...strings) => {
 	const [firstString] = strings;
 
@@ -213,6 +204,10 @@ const chalkTag = (chalk, ...strings) => {
 		);
 	}
 
+	if (template === undefined) {
+		template = require('./templates');
+	}
+
 	return template(chalk, parts.join(''));
 };
 
@@ -223,4 +218,16 @@ chalk.supportsColor = stdoutColor;
 chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap
 chalk.stderr.supportsColor = stderrColor;
 
+// For TypeScript
+chalk.Level = {
+	None: 0,
+	Basic: 1,
+	Ansi256: 2,
+	TrueColor: 3,
+	0: 'None',
+	1: 'Basic',
+	2: 'Ansi256',
+	3: 'TrueColor'
+};
+
 module.exports = chalk;


Test patch:
diff --git a/test/level.js b/test/level.js
index 65d4720..4fe8fae 100644
--- a/test/level.js
+++ b/test/level.js
@@ -43,3 +43,15 @@ test('disable colors if they are not supported', async t => {
 	const {stdout} = await execa.node(path.join(__dirname, '_fixture'));
 	t.is(stdout, 'testout testerr');
 });
+
+test('chalk.Level enum object', t => {
+	const {Level} = chalk;
+	t.is(Level.None, 0);
+	t.is(Level.Basic, 1);
+	t.is(Level.Ansi256, 2);
+	t.is(Level.TrueColor, 3);
+	t.is(Level[Level.None], 'None');
+	t.is(Level[Level.Basic], 'Basic');
+	t.is(Level[Level.Ansi256], 'Ansi256');
+	t.is(Level[Level.TrueColor], 'TrueColor');
+});