Repository: stylelint/vscode-stylelint (ts)

Issue:
[Feature]: add a stylelint.reportDescriptionlessDisables option
### What problem does this feature solve?

Stylelint has a `reportDescriptionlessDisables` option (added in https://github.com/stylelint/stylelint/pull/4907), which `vscode-stylelint` does not yet support.

### How does this feature solve the problem?

Adding a `stylelint.reportDescriptionlessDisables` option in a way similar to other options would make it so this issue could be reported when writing code.

### What are some alternatives to this feature?

_No response_

### Code of Conduct

- [X] I agree to follow vscode-stylelint's Code of Conduct

Pull request:
Add "stylelint.reportDescriptionlessDisables" option
> Which issue, if any, is this issue related to?

Closes #441

> Is there anything in the PR that needs further explanation?

Did in a way similar to other options, adding similar tests, adding it to the integration ones, and testing via running an extension development host:

<img width="617" alt="Screenshot 2023-02-12 at 18 42 34" src="https://user-images.githubusercontent.com/177485/218327882-d486d32d-65da-4c89-af8e-2a23635895ba.png">


Patch:
diff --git a/README.md b/README.md
index 37239df..23f9e8d 100644
--- a/README.md
+++ b/README.md
@@ -168,6 +168,13 @@ e.g.
   "stylelint.customSyntax": "${workspaceFolder}/custom-syntax.js"
 ```
 
+### `stylelint.reportDescriptionlessDisables`
+
+> Type: `boolean`  
+> Default: `false`
+
+Sets the Stylelint [`reportDescriptionlessDisables`] option. If `true`, Stylelint reports `stylelint-disable` comments without a description.
+
 ### `stylelint.ignoreDisables`
 
 > Type: `boolean`  
@@ -325,6 +332,7 @@ Thank you to all our backers! [Become a backer](https://opencollective.com/style
 [`config`]: https://stylelint.io/user-guide/usage/node-api#config
 [`configfile`]: https://stylelint.io/user-guide/usage/options#configfile
 [`configbasedir`]: https://stylelint.io/user-guide/usage/options#configbasedir
+[`reportdescriptionlessdisables`]: https://stylelint.io/user-guide/usage/options#reportdescriptionlessdisables
 [`ignoredisables`]: https://stylelint.io/user-guide/usage/options#ignoredisables
 [`reportneedlessdisables`]: https://stylelint.io/user-guide/usage/options#reportneedlessdisables
 [`reportinvalidscopedisables`]: https://stylelint.io/user-guide/usage/options#reportInvalidScopeDisables
diff --git a/package.json b/package.json
index 3163562..0215d81 100644
--- a/package.json
+++ b/package.json
@@ -101,11 +101,17 @@
           "default": "",
           "description": "An absolute path to a custom PostCSS-compatible syntax module."
         },
+        "stylelint.reportDescriptionlessDisables": {
+          "scope": "resource",
+          "type": "boolean",
+          "default": false,
+          "description": "Report `stylelint-disable` comments without a description."
+        },
         "stylelint.ignoreDisables": {
           "scope": "resource",
           "type": "boolean",
           "default": false,
-          "description": "Ignore `stylelint-disable` (e.g. `/* stylelint-disable block-no-empty */`) comments"
+          "description": "Ignore `stylelint-disable` (e.g. `/* stylelint-disable block-no-empty */`) comments."
         },
         "stylelint.reportNeedlessDisables": {
           "scope": "resource",
diff --git a/src/server/server.ts b/src/server/server.ts
index 5e398ff..e0e087d 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -38,6 +38,7 @@ const defaultOptions: LanguageServerOptions = {
 	customSyntax: '',
 	ignoreDisables: false,
 	packageManager: 'npm',
+	reportDescriptionlessDisables: false,
 	reportInvalidScopeDisables: false,
 	reportNeedlessDisables: false,
 	snippet: ['css', 'postcss'],
diff --git a/src/server/types.ts b/src/server/types.ts
index f1ee25c..4f9d107 100644
--- a/src/server/types.ts
+++ b/src/server/types.ts
@@ -227,6 +227,7 @@ export type LanguageServerOptions = {
 	customSyntax?: string;
 	ignoreDisables?: boolean;
 	packageManager: PackageManager;
+	reportDescriptionlessDisables?: boolean;
 	reportInvalidScopeDisables?: boolean;
 	reportNeedlessDisables?: boolean;
 	snippet: string[];
diff --git a/src/utils/stylelint/build-stylelint-options.ts b/src/utils/stylelint/build-stylelint-options.ts
index 0acf073..c5df06c 100644
--- a/src/utils/stylelint/build-stylelint-options.ts
+++ b/src/utils/stylelint/build-stylelint-options.ts
@@ -20,6 +20,7 @@ export async function buildStylelintOptions(
 		configBasedir,
 		customSyntax,
 		ignoreDisables,
+		reportDescriptionlessDisables,
 		reportNeedlessDisables,
 		reportInvalidScopeDisables,
 	}: RunnerOptions = {},
@@ -49,6 +50,9 @@ export async function buildStylelintOptions(
 
 		ignoreDisables: ignoreDisables ?? baseOptions.ignoreDisables,
 
+		reportDescriptionlessDisables:
+			reportDescriptionlessDisables ?? baseOptions.reportDescriptionlessDisables,
+
 		reportNeedlessDisables: reportNeedlessDisables ?? baseOptions.reportNeedlessDisables,
 
 		reportInvalidScopeDisables:
diff --git a/src/utils/stylelint/types.ts b/src/utils/stylelint/types.ts
index 3e520fb..cd9d4f7 100644
--- a/src/utils/stylelint/types.ts
+++ b/src/utils/stylelint/types.ts
@@ -39,6 +39,7 @@ export type RunnerOptions = {
 	customSyntax?: string;
 	ignoreDisables?: boolean;
 	packageManager?: PackageManager;
+	reportDescriptionlessDisables?: boolean;
 	reportInvalidScopeDisables?: boolean;
 	reportNeedlessDisables?: boolean;
 	snippet?: string[];


Test patch:
diff --git a/src/server/__tests__/__snapshots__/server.ts.snap b/src/server/__tests__/__snapshots__/server.ts.snap
index 4512e85..7643a2a 100644
--- a/src/server/__tests__/__snapshots__/server.ts.snap
+++ b/src/server/__tests__/__snapshots__/server.ts.snap
@@ -164,6 +164,7 @@ Array [
       "customSyntax": "",
       "ignoreDisables": false,
       "packageManager": "npm",
+      "reportDescriptionlessDisables": false,
       "reportInvalidScopeDisables": false,
       "reportNeedlessDisables": false,
       "snippet": Array [
@@ -195,6 +196,7 @@ Array [
       "customSyntax": "",
       "ignoreDisables": false,
       "packageManager": "npm",
+      "reportDescriptionlessDisables": false,
       "reportInvalidScopeDisables": false,
       "reportNeedlessDisables": false,
       "snippet": Array [
@@ -232,6 +234,7 @@ Array [
       "customSyntax": "",
       "ignoreDisables": false,
       "packageManager": "npm",
+      "reportDescriptionlessDisables": false,
       "reportInvalidScopeDisables": false,
       "reportNeedlessDisables": false,
       "snippet": Array [
@@ -262,6 +265,7 @@ Array [
       "customSyntax": "",
       "ignoreDisables": false,
       "packageManager": "npm",
+      "reportDescriptionlessDisables": false,
       "reportInvalidScopeDisables": false,
       "reportNeedlessDisables": false,
       "snippet": Array [
@@ -317,6 +321,7 @@ Object {
   "customSyntax": "",
   "ignoreDisables": false,
   "packageManager": "yarn",
+  "reportDescriptionlessDisables": false,
   "reportInvalidScopeDisables": false,
   "reportNeedlessDisables": false,
   "snippet": Array [
@@ -344,6 +349,7 @@ Object {
   "customSyntax": "",
   "ignoreDisables": false,
   "packageManager": "npm",
+  "reportDescriptionlessDisables": false,
   "reportInvalidScopeDisables": false,
   "reportNeedlessDisables": false,
   "snippet": Array [
@@ -370,6 +376,7 @@ Object {
   "customSyntax": "",
   "ignoreDisables": false,
   "packageManager": "npm",
+  "reportDescriptionlessDisables": false,
   "reportInvalidScopeDisables": false,
   "reportNeedlessDisables": false,
   "snippet": Array [
@@ -397,6 +404,7 @@ Object {
   "customSyntax": "",
   "ignoreDisables": false,
   "packageManager": "npm",
+  "reportDescriptionlessDisables": false,
   "reportInvalidScopeDisables": false,
   "reportNeedlessDisables": false,
   "snippet": Array [
@@ -423,6 +431,7 @@ Object {
   "customSyntax": "",
   "ignoreDisables": false,
   "packageManager": "npm",
+  "reportDescriptionlessDisables": false,
   "reportInvalidScopeDisables": false,
   "reportNeedlessDisables": false,
   "snippet": Array [
diff --git a/src/utils/documents/__tests__/get-fixes.ts b/src/utils/documents/__tests__/get-fixes.ts
index 86aa618..3cc503d 100644
--- a/src/utils/documents/__tests__/get-fixes.ts
+++ b/src/utils/documents/__tests__/get-fixes.ts
@@ -22,6 +22,7 @@ describe('getFixes', () => {
 				customSyntax: 'postcss-scss',
 			},
 			fix: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: false,
 		};
diff --git a/src/utils/stylelint/__tests__/__snapshots__/stylelint-runner.ts.snap b/src/utils/stylelint/__tests__/__snapshots__/stylelint-runner.ts.snap
index 7fb422c..68a1828 100644
--- a/src/utils/stylelint/__tests__/__snapshots__/stylelint-runner.ts.snap
+++ b/src/utils/stylelint/__tests__/__snapshots__/stylelint-runner.ts.snap
@@ -14,6 +14,7 @@ Object {
   "formatter": [Function],
   "ignoreDisables": undefined,
   "ignorePath": "/.stylelintignore",
+  "reportDescriptionlessDisables": undefined,
   "reportInvalidScopeDisables": undefined,
   "reportNeedlessDisables": undefined,
 }
diff --git a/src/utils/stylelint/__tests__/build-stylelint-options.ts b/src/utils/stylelint/__tests__/build-stylelint-options.ts
index 77db4d8..5895e8d 100644
--- a/src/utils/stylelint/__tests__/build-stylelint-options.ts
+++ b/src/utils/stylelint/__tests__/build-stylelint-options.ts
@@ -84,6 +84,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: false,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
@@ -103,6 +104,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: true,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: true,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: true,
 		};
@@ -113,6 +115,7 @@ describe('buildStylelintOptions', () => {
 			configBasedir: '/workspace',
 			customSyntax: 'postcss-html',
 			ignoreDisables: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
@@ -140,6 +143,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: true,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: true,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: true,
 		};
@@ -150,6 +154,7 @@ describe('buildStylelintOptions', () => {
 			configBasedir: '/workspace',
 			customSyntax: '${workspaceFolder}/postcss-html',
 			ignoreDisables: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
@@ -181,6 +186,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: true,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: true,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: true,
 		};
@@ -191,6 +197,7 @@ describe('buildStylelintOptions', () => {
 			configBasedir: '/workspace',
 			customSyntax: '${workspaceFolder}/postcss-html',
 			ignoreDisables: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
@@ -220,6 +227,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: true,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: true,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: true,
 		};
@@ -230,6 +238,7 @@ describe('buildStylelintOptions', () => {
 			configBasedir: './base',
 			customSyntax: '/workspace/postcss-html',
 			ignoreDisables: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
@@ -259,6 +268,7 @@ describe('buildStylelintOptions', () => {
 			customSyntax: 'postcss-scss',
 			ignoreDisables: true,
 			ignorePath: '/.stylelintignore',
+			reportDescriptionlessDisables: true,
 			reportNeedlessDisables: true,
 			reportInvalidScopeDisables: true,
 		};
@@ -269,6 +279,7 @@ describe('buildStylelintOptions', () => {
 			configBasedir: './base',
 			customSyntax: '/workspace/postcss-html',
 			ignoreDisables: false,
+			reportDescriptionlessDisables: false,
 			reportNeedlessDisables: false,
 			reportInvalidScopeDisables: false,
 		};
diff --git a/test/e2e/__tests__/__snapshots__/report-descriptionless-disables.ts.snap b/test/e2e/__tests__/__snapshots__/report-descriptionless-disables.ts.snap
new file mode 100644
index 0000000..602fefa
--- /dev/null
+++ b/test/e2e/__tests__/__snapshots__/report-descriptionless-disables.ts.snap
@@ -0,0 +1,22 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`"stylelint.reportDescriptionlessDisables" setting should report invalid-scope disables when enabled 1`] = `
+Array [
+  Object {
+    "code": "--report-descriptionless-disables",
+    "message": "Disable for \\"indentation\\" is missing a description",
+    "range": Object {
+      "end": Object {
+        "character": 5,
+        "line": 2,
+      },
+      "start": Object {
+        "character": 4,
+        "line": 2,
+      },
+    },
+    "severity": 1,
+    "source": "Stylelint",
+  },
+]
+`;
diff --git a/test/e2e/__tests__/report-descriptionless-disables.ts b/test/e2e/__tests__/report-descriptionless-disables.ts
new file mode 100644
index 0000000..7a55834
--- /dev/null
+++ b/test/e2e/__tests__/report-descriptionless-disables.ts
@@ -0,0 +1,17 @@
+import path from 'path';
+import { normalizeDiagnostic } from '../utils';
+
+describe('"stylelint.reportDescriptionlessDisables" setting', () => {
+	it('should report invalid-scope disables when enabled', async () => {
+		const { document } = await openDocument(
+			path.resolve(workspaceDir, 'descriptionless-disables/test.css'),
+		);
+		const diagnostics = await waitForDiagnostics(document);
+
+		expect(
+			diagnostics
+				.map(normalizeDiagnostic)
+				.filter((diagnostic) => diagnostic?.code === '--report-descriptionless-disables'),
+		).toMatchSnapshot();
+	});
+});
diff --git a/test/e2e/workspace/descriptionless-disables/.vscode/settings.json b/test/e2e/workspace/descriptionless-disables/.vscode/settings.json
new file mode 100644
index 0000000..dd04f77
--- /dev/null
+++ b/test/e2e/workspace/descriptionless-disables/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+	"stylelint.reportDescriptionlessDisables": true,
+}
diff --git a/test/e2e/workspace/descriptionless-disables/stylelint.config.js b/test/e2e/workspace/descriptionless-disables/stylelint.config.js
new file mode 100644
index 0000000..4939bf5
--- /dev/null
+++ b/test/e2e/workspace/descriptionless-disables/stylelint.config.js
@@ -0,0 +1,7 @@
+'use strict';
+
+module.exports = {
+	rules: {
+		indentation: [4],
+	},
+};
diff --git a/test/e2e/workspace/descriptionless-disables/test.css b/test/e2e/workspace/descriptionless-disables/test.css
new file mode 100644
index 0000000..54021f0
--- /dev/null
+++ b/test/e2e/workspace/descriptionless-disables/test.css
@@ -0,0 +1,10 @@
+/* prettier-ignore */
+a {
+    /* stylelint-disable-next-line indentation */
+  color: #fff;
+}
+
+a {
+    /* stylelint-disable-next-line indentation -- with a description */
+  color: #fff;
+}
diff --git a/test/e2e/workspace/workspace.code-workspace b/test/e2e/workspace/workspace.code-workspace
index e7e1e86..ead5efa 100644
--- a/test/e2e/workspace/workspace.code-workspace
+++ b/test/e2e/workspace/workspace.code-workspace
@@ -4,6 +4,7 @@
 		{ "path": "config" },
 		{ "path": "custom-syntax" },
 		{ "path": "defaults" },
+		{ "path": "descriptionless-disables" },
 		{ "path": "ignore-disables" },
 		{ "path": "report-disables" },
 		{ "path": "stylelint-path" },
diff --git a/test/integration/stylelint-vscode/__snapshots__/test.ts.snap b/test/integration/stylelint-vscode/__snapshots__/test.ts.snap
index 92bb421..22af104 100644
--- a/test/integration/stylelint-vscode/__snapshots__/test.ts.snap
+++ b/test/integration/stylelint-vscode/__snapshots__/test.ts.snap
@@ -342,6 +342,61 @@ Object {
 }
 `;
 
+exports[`StylelintRunner with reportDescriptionlessDisables should work properly if reportDescriptionlessDisables is true 1`] = `
+Object {
+  "diagnostics": Array [
+    Object {
+      "code": "--report-descriptionless-disables",
+      "message": "Disable for \\"indentation\\" is missing a description",
+      "range": Object {
+        "end": Object {
+          "character": 5,
+          "line": 2,
+        },
+        "start": Object {
+          "character": 4,
+          "line": 2,
+        },
+      },
+      "severity": 1,
+      "source": "Stylelint",
+    },
+    Object {
+      "code": "--report-descriptionless-disables",
+      "message": "Disable for \\"indentation\\" is missing a description",
+      "range": Object {
+        "end": Object {
+          "character": 1,
+          "line": 5,
+        },
+        "start": Object {
+          "character": 0,
+          "line": 5,
+        },
+      },
+      "severity": 1,
+      "source": "Stylelint",
+    },
+    Object {
+      "code": "--report-descriptionless-disables",
+      "message": "Disable for \\"indentation\\" is missing a description",
+      "range": Object {
+        "end": Object {
+          "character": 15,
+          "line": 11,
+        },
+        "start": Object {
+          "character": 14,
+          "line": 11,
+        },
+      },
+      "severity": 1,
+      "source": "Stylelint",
+    },
+  ],
+}
+`;
+
 exports[`StylelintRunner with reportInvalidScopeDisables should work properly if reportInvalidScopeDisables is true 1`] = `
 Object {
   "diagnostics": Array [
diff --git a/test/integration/stylelint-vscode/test.ts b/test/integration/stylelint-vscode/test.ts
index 2e525fe..716780f 100644
--- a/test/integration/stylelint-vscode/test.ts
+++ b/test/integration/stylelint-vscode/test.ts
@@ -436,6 +436,52 @@ describe('StylelintRunner with customSyntax', () => {
 	});
 });
 
+describe('StylelintRunner with reportDescriptionlessDisables', () => {
+	test('should work properly if reportDescriptionlessDisables is true', async () => {
+		expect.assertions(1);
+		const runner = new StylelintRunner();
+		const result = await runner.lintDocument(
+			createDocument(
+				'test.css',
+				'css',
+				`
+.baz {
+    /* stylelint-disable-next-line indentation */
+  color: red;
+}
+/* stylelint-disable indentation */
+.baz {
+  color: red;
+}
+/* stylelint-enable indentation */
+.baz {
+  color: red; /* stylelint-disable-line indentation */
+}
+
+.baz {
+    /* stylelint-disable-next-line indentation -- with a description */
+  color: red;
+}
+/* stylelint-disable indentation -- with a description */
+.baz {
+  color: red;
+}
+/* stylelint-enable indentation */
+.baz {
+  color: red; /* stylelint-disable-line indentation -- with a description */
+}
+`,
+			),
+			{
+				config: { rules: { indentation: [4] } },
+				reportDescriptionlessDisables: true,
+			},
+		);
+
+		expect(result).toMatchSnapshot();
+	});
+});
+
 describe('StylelintRunner with reportNeedlessDisables', () => {
 	test('should work properly if reportNeedlessDisables is true', async () => {
 		expect.assertions(1);
diff --git a/test/unit/server-mocks.ts b/test/unit/server-mocks.ts
index 6683788..0863c23 100644
--- a/test/unit/server-mocks.ts
+++ b/test/unit/server-mocks.ts
@@ -501,6 +501,7 @@ export function getOptions(): LanguageServerOptions {
 		customSyntax: '',
 		ignoreDisables: false,
 		packageManager: 'npm',
+		reportDescriptionlessDisables: false,
 		reportInvalidScopeDisables: false,
 		reportNeedlessDisables: false,
 		snippet: ['css', 'postcss'],