Repository: chalk/chalk (js)

Issue:
chalk.enabled=true or { enabled: true} does not work when color support is initially detected as false
<!-- Issuehunt Badges -->

[<img alt="Issuehunt badges" src="https://issuehunt.io/r/chalk/chalk/issues/234/badge.svg" />](https://issuehunt.io/r/chalk/chalk/issues/234)

<!-- /Issuehunt Badges -->

## Environment
**Chalk:** 2.3.0
**Node.js:** 6.12.0 and 8.9.3

## Issue
**chalk.js**   
```js
const chalk = require('chalk');
chalk.enabled = true;
console.log(chalk`{green hello}`);
```
--or--
```js
const chalk = new require('chalk').constructor({ enabled: true });
```

If you run **chalk.js** as:

```
node chalk.js
```

The output is a green `hello`.

If you run this in a clean environment (which causes **supports-color** to report that colors are not supported):

```
env -i node chalk.js
```

The output is a un-colored `hello`.

## Details
I traced this to https://github.com/chalk/chalk/blob/master/index.js#L177, where `this.enabled` is `false`, and `this` is `builder`. It seems like, somehow, the initial value for `enabled` is not getting changed or is not filtering down to the `enabled` property defined on `builder`.

As expected, using the environment variable `FORCE_COLOR` or the `--color` parameter does work, as it gets `enabled` to the right value, initially.

Interestingly, running it as (to force it on from the start):

```
env -i node chalk.js --color
```
...and then doing:
```js
chalk.enabled = false;
```
..._does_ actually turn it off. Now I'm thinking there's some issue with truthy and falsy. I'm not sure.

## Aside
It would definitely be easier for me to dig more on this if there weren't _so much_ indirection in the way this is implemented :innocent:; but, I'm sure some or most of that is necessary to get this to work so cleanly.
<!-- Issuehunt content -->



> There is a $40.00 open bounty on this issue. Add more on [Issuehunt](https://issuehunt.io/r/chalk/chalk/issues/234).
> - 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 content-->

Pull request:
Remove the `.enabled` property in favor of `.level`
Closes #234.

Also got to enable a test previously marking as failing, so that's cool. We were already pretty much entirely using `.level` so this was quite low-touch.

Patch:
diff --git a/index.d.ts b/index.d.ts
index 455ba9a..58da80e 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -24,13 +24,6 @@ declare namespace chalk {
 	type Level = LevelEnum;
 
 	interface Options {
-		/**
-		Enable or disable Chalk.
-
-		@default true
-		*/
-		enabled?: boolean;
-
 		/**
 		Specify the color support for Chalk.
 		By default, color support is automatically detected based on the environment.
@@ -98,13 +91,6 @@ declare namespace chalk {
 		*/
 		Instance: Instance;
 
-		/**
-		Enable or disable Chalk.
-
-		@default true
-		*/
-		enabled: boolean;
-
 		/**
 		The color support for Chalk.
 		By default, color support is automatically detected based on the environment.
@@ -248,7 +234,7 @@ declare namespace chalk {
 		readonly strikethrough: Chalk;
 
 		/**
-		Modifier: Prints the text only when Chalk is enabled.
+		Modifier: Prints the text only when Chalk has a color support level > 0.
 		Can be useful for things that are purely cosmetic.
 		*/
 		readonly visible: Chalk;
diff --git a/readme.md b/readme.md
index ede4ee1..0de3051 100644
--- a/readme.md
+++ b/readme.md
@@ -120,21 +120,11 @@ Chain [styles](#styles) and call the last one as a method with a string argument
 
 Multiple arguments will be separated by space.
 
-### chalk.enabled
-
-Color support is automatically detected, as is the level (see `chalk.level`). However, if you'd like to simply enable/disable Chalk, you can do so via the `.enabled` property. When `chalk.enabled` is `true`, `chalk.level` must *also* be greater than `0` for colored output to be produced.
-
-Chalk is enabled by default unless explicitly disabled via `new chalk.Instance()` or `chalk.level` is `0`.
-
-If you need to change this in a reusable module, create a new instance:
-
-```js
-const ctx = new chalk.Instance({enabled: false});
-```
-
 ### chalk.level
 
-Color support is automatically detected, but you can override it by setting the `level` property. You should however only do this in your own code as it applies globally to all Chalk consumers. When `chalk.level` is greater than `0`, `chalk.enabled` must *also* be `true` for colored output to be produced.
+Specifies the level of color support.
+
+Color support is automatically detected, but you can override it by setting the `level` property. You should however only do this in your own code as it applies globally to all Chalk consumers.
 
 If you need to change this in a reusable module, create a new instance:
 
@@ -170,7 +160,7 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=
 - `inverse`- Inverse background and foreground colors.
 - `hidden` - Prints the text, but makes it invisible.
 - `strikethrough` - Puts a horizontal line through the center of the text. *(Not widely supported)*
-- `visible`- Prints the text only when Chalk is enabled. Can be useful for things that are purely cosmetic.
+- `visible`- Prints the text only when Chalk has a color level > 0. Can be useful for things that are purely cosmetic.
 
 ### Colors
 
diff --git a/source/index.js b/source/index.js
index e8d47c2..9b4b46b 100644
--- a/source/index.js
+++ b/source/index.js
@@ -28,7 +28,6 @@ const applyOptions = (object, options = {}) => {
 	// Detect level if not set manually
 	const colorLevel = stdoutColor ? stdoutColor.level : 0;
 	object.level = options.level === undefined ? colorLevel : options.level;
-	object.enabled = 'enabled' in options ? options.enabled : object.level > 0;
 };
 
 class ChalkClass {
@@ -120,15 +119,6 @@ const proto = Object.defineProperties(() => {}, {
 		set(level) {
 			this._generator.level = level;
 		}
-	},
-	enabled: {
-		enumerable: true,
-		get() {
-			return this._generator.enabled;
-		},
-		set(enabled) {
-			this._generator.enabled = enabled;
-		}
 	}
 });
 
@@ -171,7 +161,7 @@ const createBuilder = (self, _styler, _isEmpty) => {
 };
 
 const applyStyle = (self, string) => {
-	if (!self.enabled || self.level <= 0 || !string) {
+	if (self.level <= 0 || !string) {
 		return self._isEmpty ? '' : string;
 	}
 
diff --git a/source/util.js b/source/util.js
index d617156..ca466fd 100644
--- a/source/util.js
+++ b/source/util.js
@@ -10,7 +10,7 @@ const stringReplaceAll = (string, substring, replacer) => {
 	let endIndex = 0;
 	let returnValue = '';
 	do {
-		returnValue += string.substr(endIndex, index - endIndex) + replacer;
+		returnValue += string.substr(endIndex, index - endIndex) + substring + replacer;
 		endIndex = index + substringLength;
 		index = string.indexOf(substring, endIndex);
 	} while (index !== -1);


Test patch:
diff --git a/index.test-d.ts b/index.test-d.ts
index fd19d04..5ab486d 100644
--- a/index.test-d.ts
+++ b/index.test-d.ts
@@ -24,7 +24,6 @@ expectError(chalk.reset.supportsColor);
 expectType<chalk.Chalk>(new chalk.Instance({level: 1}));
 
 // -- Properties --
-expectType<boolean>(chalk.enabled);
 expectType<chalk.Level>(chalk.level);
 
 // -- Template literal --
diff --git a/test/chalk.js b/test/chalk.js
index 008a128..2992a09 100644
--- a/test/chalk.js
+++ b/test/chalk.js
@@ -37,7 +37,7 @@ test('support nesting styles', t => {
 test('support nesting styles of the same type (color, underline, bg)', t => {
 	t.is(
 		chalk.red('a' + chalk.yellow('b' + chalk.green('c') + 'b') + 'c'),
-		'\u001B[31ma\u001B[33mb\u001B[32mc\u001B[33mb\u001B[31mc\u001B[39m'
+		'\u001B[31ma\u001B[33mb\u001B[32mc\u001B[39m\u001B[31m\u001B[33mb\u001B[39m\u001B[31mc\u001B[39m'
 	);
 });
 
diff --git a/test/enabled.js b/test/enabled.js
deleted file mode 100644
index 3d9c55f..0000000
--- a/test/enabled.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import test from 'ava';
-
-// Spoof supports-color
-require('./_supports-color')(__dirname);
-
-const chalk = require('../source');
-
-test('don\'t output colors when manually disabled', t => {
-	chalk.enabled = false;
-	t.is(chalk.red('foo'), 'foo');
-	chalk.enabled = true;
-});
-
-test('enable/disable colors based on overall chalk enabled property, not individual instances', t => {
-	chalk.enabled = false;
-	const {red} = chalk;
-	t.false(red.enabled);
-	chalk.enabled = true;
-	t.true(red.enabled);
-	chalk.enabled = true;
-});
-
-test('propagate enable/disable changes from child colors', t => {
-	chalk.enabled = false;
-	const {red} = chalk;
-	t.false(red.enabled);
-	t.false(chalk.enabled);
-	red.enabled = true;
-	t.true(red.enabled);
-	t.true(chalk.enabled);
-	chalk.enabled = false;
-	t.false(red.enabled);
-	t.false(chalk.enabled);
-	chalk.enabled = true;
-});
diff --git a/test/instance.js b/test/instance.js
index 8d2c4e1..e936333 100644
--- a/test/instance.js
+++ b/test/instance.js
@@ -6,21 +6,13 @@ require('./_supports-color')(__dirname);
 const chalk = require('../source');
 
 test('create an isolated context where colors can be disabled (by level)', t => {
-	const instance = new chalk.Instance({level: 0, enabled: true});
+	const instance = new chalk.Instance({level: 0});
 	t.is(instance.red('foo'), 'foo');
 	t.is(chalk.red('foo'), '\u001B[31mfoo\u001B[39m');
 	instance.level = 2;
 	t.is(instance.red('foo'), '\u001B[31mfoo\u001B[39m');
 });
 
-test('create an isolated context where colors can be disabled (by enabled flag)', t => {
-	const instance = new chalk.Instance({enabled: false});
-	t.is(instance.red('foo'), 'foo');
-	t.is(chalk.red('foo'), '\u001B[31mfoo\u001B[39m');
-	instance.enabled = true;
-	t.is(instance.red('foo'), '\u001B[31mfoo\u001B[39m');
-});
-
 test('the `level` option should be a number from 0 to 3', t => {
 	/* eslint-disable no-new */
 	t.throws(() => {
diff --git a/test/level.js b/test/level.js
index bb6df47..828de24 100644
--- a/test/level.js
+++ b/test/level.js
@@ -14,7 +14,7 @@ test('don\'t output colors when manually disabled', t => {
 	chalk.level = oldLevel;
 });
 
-test('enable/disable colors based on overall chalk enabled property, not individual instances', t => {
+test('enable/disable colors based on overall chalk .level property, not individual instances', t => {
 	const oldLevel = chalk.level;
 	chalk.level = 1;
 	const {red} = chalk;
diff --git a/test/no-color-support.js b/test/no-color-support.js
index dae9bda..7318452 100644
--- a/test/no-color-support.js
+++ b/test/no-color-support.js
@@ -10,7 +10,7 @@ require('./_supports-color')(__dirname, {
 
 const chalk = require('../source');
 
-test.failing('colors can be forced by using chalk.enabled', t => {
-	chalk.enabled = true;
+test('colors can be forced by using chalk.level', t => {
+	chalk.level = 1;
 	t.is(chalk.green('hello'), '\u001B[32mhello\u001B[39m');
 });
diff --git a/test/visible.js b/test/visible.js
index 14ecc3c..7987712 100644
--- a/test/visible.js
+++ b/test/visible.js
@@ -5,40 +5,34 @@ require('./_supports-color')(__dirname);
 
 const chalk = require('../source');
 
-test('visible: normal output when enabled', t => {
-	const instance = new chalk.Instance({level: 3, enabled: true});
+test('visible: normal output when level > 0', t => {
+	const instance = new chalk.Instance({level: 3});
 	t.is(instance.visible.red('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.red.visible('foo'), '\u001B[31mfoo\u001B[39m');
 });
 
-test('visible: no output when disabled', t => {
-	const instance = new chalk.Instance({level: 3, enabled: false});
-	t.is(instance.red.visible('foo'), '');
-	t.is(instance.visible.red('foo'), '');
-});
-
 test('visible: no output when level is too low', t => {
-	const instance = new chalk.Instance({level: 0, enabled: true});
+	const instance = new chalk.Instance({level: 0});
 	t.is(instance.visible.red('foo'), '');
 	t.is(instance.red.visible('foo'), '');
 });
 
-test('test switching back and forth between enabled and disabled', t => {
-	const instance = new chalk.Instance({level: 3, enabled: true});
+test('test switching back and forth between level == 0 and level > 0', t => {
+	const instance = new chalk.Instance({level: 3});
 	t.is(instance.red('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.visible.red('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.red.visible('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.visible('foo'), 'foo');
 	t.is(instance.red('foo'), '\u001B[31mfoo\u001B[39m');
 
-	instance.enabled = false;
+	instance.level = 0;
 	t.is(instance.red('foo'), 'foo');
 	t.is(instance.visible('foo'), '');
 	t.is(instance.visible.red('foo'), '');
 	t.is(instance.red.visible('foo'), '');
 	t.is(instance.red('foo'), 'foo');
 
-	instance.enabled = true;
+	instance.level = 3;
 	t.is(instance.red('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.visible.red('foo'), '\u001B[31mfoo\u001B[39m');
 	t.is(instance.red.visible('foo'), '\u001B[31mfoo\u001B[39m');