diff --git a/crates/oxc_linter/src/config/oxlintrc.rs b/crates/oxc_linter/src/config/oxlintrc.rs
index ba6f631a220b9..33a20c84b7046 100644
--- a/crates/oxc_linter/src/config/oxlintrc.rs
+++ b/crates/oxc_linter/src/config/oxlintrc.rs
@@ -204,6 +204,7 @@ impl Oxlintrc {
/// Panics if the schema generation fails.
pub fn generate_schema_json() -> String {
let mut schema = schema_for!(Oxlintrc);
+
// Allow comments and trailing commas for vscode-json-languageservice
// NOTE: This is NOT part of standard JSON Schema specification
// https://github.com/microsoft/vscode-json-languageservice/blob/fb83547762901f32d8449d57e24666573016b10c/src/jsonLanguageTypes.ts#L151-L159
@@ -212,7 +213,43 @@ impl Oxlintrc {
.schema
.extensions
.insert("allowTrailingCommas".to_string(), serde_json::Value::Bool(true));
- serde_json::to_string_pretty(&schema).unwrap()
+
+ // Inject markdownDescription fields for better editor support (e.g., VS Code)
+ let mut json = serde_json::to_value(&schema).unwrap();
+ Self::inject_markdown_descriptions(&mut json);
+
+ serde_json::to_string_pretty(&json).unwrap()
+ }
+
+ /// Recursively inject `markdownDescription` fields into the JSON schema.
+ /// This is a non-standard field that some editors (like VS Code) use to render
+ /// markdown in hover tooltips.
+ fn inject_markdown_descriptions(value: &mut serde_json::Value) {
+ match value {
+ serde_json::Value::Object(map) => {
+ // If this object has a `description` field, copy it to `markdownDescription`
+ if let Some(serde_json::Value::String(desc_str)) = map.get("description") {
+ map.insert(
+ "markdownDescription".to_string(),
+ serde_json::Value::String(desc_str.clone()),
+ );
+ }
+
+ // Recursively process all values in the object
+ for value in map.values_mut() {
+ Self::inject_markdown_descriptions(value);
+ }
+ }
+ serde_json::Value::Array(items) => {
+ // Recursively process all items in the array
+ for item in items {
+ Self::inject_markdown_descriptions(item);
+ }
+ }
+ _ => {
+ // Primitive values don't need processing
+ }
+ }
}
/// Merges two [Oxlintrc] files together
diff --git a/crates/oxc_linter/src/snapshots/schema_json.snap b/crates/oxc_linter/src/snapshots/schema_json.snap
index 012f341ebcc56..bc9b1ce9e783e 100644
--- a/crates/oxc_linter/src/snapshots/schema_json.snap
+++ b/crates/oxc_linter/src/snapshots/schema_json.snap
@@ -25,14 +25,16 @@ expression: json
{
"$ref": "#/definitions/OxlintEnv"
}
- ]
+ ],
+ "markdownDescription": "Environments enable and disable collections of global variables."
},
"extends": {
"description": "Paths of configuration files that this configuration file extends (inherits from). The files\nare resolved relative to the location of the configuration file that contains the `extends`\nproperty. The configuration files are merged from the first to the last, with the last file\noverriding the previous ones.",
"type": "array",
"items": {
"type": "string"
- }
+ },
+ "markdownDescription": "Paths of configuration files that this configuration file extends (inherits from). The files\nare resolved relative to the location of the configuration file that contains the `extends`\nproperty. The configuration files are merged from the first to the last, with the last file\noverriding the previous ones."
},
"globals": {
"description": "Enabled or disabled specific global variables.",
@@ -41,7 +43,8 @@ expression: json
{
"$ref": "#/definitions/OxlintGlobals"
}
- ]
+ ],
+ "markdownDescription": "Enabled or disabled specific global variables."
},
"ignorePatterns": {
"description": "Globs to ignore during linting. These are resolved from the configuration file path.",
@@ -49,7 +52,8 @@ expression: json
"type": "array",
"items": {
"type": "string"
- }
+ },
+ "markdownDescription": "Globs to ignore during linting. These are resolved from the configuration file path."
},
"jsPlugins": {
"description": "JS plugins.\n\nNote: JS plugins are experimental and not subject to semver.\nThey are not supported in language server at present.",
@@ -60,7 +64,8 @@ expression: json
"items": {
"type": "string"
},
- "uniqueItems": true
+ "uniqueItems": true,
+ "markdownDescription": "JS plugins.\n\nNote: JS plugins are experimental and not subject to semver.\nThey are not supported in language server at present."
},
"overrides": {
"description": "Add, remove, or otherwise reconfigure rules for specific files or groups of files.",
@@ -68,7 +73,8 @@ expression: json
{
"$ref": "#/definitions/OxlintOverrides"
}
- ]
+ ],
+ "markdownDescription": "Add, remove, or otherwise reconfigure rules for specific files or groups of files."
},
"plugins": {
"description": "Enabled built-in plugins for Oxlint.\nYou can view the list of available plugins on\n[the website](https://oxc.rs/docs/guide/usage/linter/plugins.html#supported-plugins).\n\nNOTE: Setting the `plugins` field will overwrite the base set of plugins.\nThe `plugins` array should reflect all of the plugins you want to use.",
@@ -80,7 +86,8 @@ expression: json
{
"type": "null"
}
- ]
+ ],
+ "markdownDescription": "Enabled built-in plugins for Oxlint.\nYou can view the list of available plugins on\n[the website](https://oxc.rs/docs/guide/usage/linter/plugins.html#supported-plugins).\n\nNOTE: Setting the `plugins` field will overwrite the base set of plugins.\nThe `plugins` array should reflect all of the plugins you want to use."
},
"rules": {
"description": "Example\n\n`.oxlintrc.json`\n\n```json\n{\n\"$schema\": \"./node_modules/oxlint/configuration_schema.json\",\n\"rules\": {\n\"eqeqeq\": \"warn\",\n\"import/no-cycle\": \"error\",\n\"prefer-const\": [\"error\", { \"ignoreReadBeforeAssign\": true }]\n}\n}\n```\n\nSee [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html) for the list of\nrules.",
@@ -89,7 +96,8 @@ expression: json
{
"$ref": "#/definitions/OxlintRules"
}
- ]
+ ],
+ "markdownDescription": "Example\n\n`.oxlintrc.json`\n\n```json\n{\n\"$schema\": \"./node_modules/oxlint/configuration_schema.json\",\n\"rules\": {\n\"eqeqeq\": \"warn\",\n\"import/no-cycle\": \"error\",\n\"prefer-const\": [\"error\", { \"ignoreReadBeforeAssign\": true }]\n}\n}\n```\n\nSee [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html) for the list of\nrules."
},
"settings": {
"default": {
@@ -140,14 +148,16 @@ expression: json
"warn",
"error",
"deny"
- ]
+ ],
+ "markdownDescription": "Oxlint rule.\n- \"allow\" or \"off\": Turn off the rule.\n- \"warn\": Turn the rule on as a warning (doesn't affect exit code).\n- \"error\" or \"deny\": Turn the rule on as an error (will exit with a failure code)."
},
{
"description": "Oxlint rule.\n \n- 0: Turn off the rule.\n- 1: Turn the rule on as a warning (doesn't affect exit code).\n- 2: Turn the rule on as an error (will exit with a failure code).",
"type": "integer",
"format": "uint32",
"maximum": 2.0,
- "minimum": 0.0
+ "minimum": 0.0,
+ "markdownDescription": "Oxlint rule.\n \n- 0: Turn off the rule.\n- 1: Turn the rule on as a warning (doesn't affect exit code).\n- 2: Turn the rule on as an error (will exit with a failure code)."
}
]
},
@@ -207,14 +217,16 @@ expression: json
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/DummyRule"
- }
+ },
+ "markdownDescription": "See [Oxlint Rules](https://oxc.rs/docs/guide/usage/linter/rules.html)"
},
"GlobSet": {
"description": "A set of glob patterns.",
"type": "array",
"items": {
"type": "string"
- }
+ },
+ "markdownDescription": "A set of glob patterns."
},
"GlobalValue": {
"type": "string",
@@ -230,37 +242,44 @@ expression: json
"augmentsExtendsReplacesDocs": {
"description": "Only for `require-(yields|returns|description|example|param|throws)` rule",
"default": false,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "Only for `require-(yields|returns|description|example|param|throws)` rule"
},
"exemptDestructuredRootsFromChecks": {
"description": "Only for `require-param-type` and `require-param-description` rule",
"default": false,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "Only for `require-param-type` and `require-param-description` rule"
},
"ignoreInternal": {
"description": "For all rules but NOT apply to `empty-tags` rule",
"default": false,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "For all rules but NOT apply to `empty-tags` rule"
},
"ignorePrivate": {
"description": "For all rules but NOT apply to `check-access` and `empty-tags` rule",
"default": false,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "For all rules but NOT apply to `check-access` and `empty-tags` rule"
},
"ignoreReplacesDocs": {
"description": "Only for `require-(yields|returns|description|example|param|throws)` rule",
"default": true,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "Only for `require-(yields|returns|description|example|param|throws)` rule"
},
"implementsReplacesDocs": {
"description": "Only for `require-(yields|returns|description|example|param|throws)` rule",
"default": false,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "Only for `require-(yields|returns|description|example|param|throws)` rule"
},
"overrideReplacesDocs": {
"description": "Only for `require-(yields|returns|description|example|param|throws)` rule",
"default": true,
- "type": "boolean"
+ "type": "boolean",
+ "markdownDescription": "Only for `require-(yields|returns|description|example|param|throws)` rule"
},
"tagNamePreference": {
"default": {},
@@ -284,7 +303,8 @@ expression: json
"items": {
"type": "string"
}
- }
+ },
+ "markdownDescription": "Map of attribute names to their DOM equivalents.\nThis is useful for non-React frameworks that use different attribute names.\n\nExample:\n\n```json\n{\n\"settings\": {\n\"jsx-a11y\": {\n\"attributes\": {\n\"for\": [\"htmlFor\", \"for\"]\n}\n}\n}\n}\n```"
},
"components": {
"description": "To have your custom components be checked as DOM elements, you can\nprovide a mapping of your component names to the DOM element name.\n\nExample:\n\n```json\n{\n\"settings\": {\n\"jsx-a11y\": {\n\"components\": {\n\"Link\": \"a\",\n\"IconButton\": \"button\"\n}\n}\n}\n}\n```",
@@ -292,16 +312,19 @@ expression: json
"type": "object",
"additionalProperties": {
"type": "string"
- }
+ },
+ "markdownDescription": "To have your custom components be checked as DOM elements, you can\nprovide a mapping of your component names to the DOM element name.\n\nExample:\n\n```json\n{\n\"settings\": {\n\"jsx-a11y\": {\n\"components\": {\n\"Link\": \"a\",\n\"IconButton\": \"button\"\n}\n}\n}\n}\n```"
},
"polymorphicPropName": {
"description": "An optional setting that define the prop your code uses to create polymorphic components.\nThis setting will be used to determine the element type in rules that\nrequire semantic context.\n\nFor example, if you set the `polymorphicPropName` to `as`, then this element:\n\n```jsx\n