Add ESLint unused imports detection for MDX documentation files#18951
Merged
harupy merged 9 commits intomlflow:masterfrom Nov 25, 2025
Merged
Add ESLint unused imports detection for MDX documentation files#18951harupy merged 9 commits intomlflow:masterfrom
harupy merged 9 commits intomlflow:masterfrom
Conversation
harupy
commented
Nov 21, 2025
Member
Author
There was a problem hiding this comment.
The main change. The motivation is, in #18946, I forgot to remove a no-longer-used ImageBox component and Copilot left many comments. I don't want that to happen.
92af0fe to
8f7edc1
Compare
905dc26 to
4bbf91c
Compare
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
4bbf91c to
599fe76
Compare
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
Contributor
harupy
commented
Nov 21, 2025
docs/eslint.config.js
Outdated
Comment on lines
+9
to
+22
| // Prevent auto-fixing MDX files as it can corrupt file contents | ||
| // Can be bypassed by setting MLFLOW_DOCS_ALLOW_ESLINT_FIX=1 environment variable | ||
| if (process.argv.includes('--fix') && !process.env.MLFLOW_DOCS_ALLOW_ESLINT_FIX) { | ||
| throw new Error( | ||
| 'ESLint --fix is disabled for MDX files because it can corrupt file contents ' + | ||
| '(e.g., removing heading markers like # or inconsistent semicolon handling).\n\n' + | ||
| 'If you want to use auto-fix anyway:\n' + | ||
| '1. Set the environment variable: MLFLOW_DOCS_ALLOW_ESLINT_FIX=1\n' + | ||
| '2. Run: MLFLOW_DOCS_ALLOW_ESLINT_FIX=1 eslint --fix\n' + | ||
| '3. Carefully review ALL changes before committing\n' + | ||
| '4. Manually restore any corrupted content\n\n' + | ||
| 'Otherwise, please fix issues manually.', | ||
| ); | ||
| } |
Member
Author
There was a problem hiding this comment.
This was a surprise but codex helped:
Autofix script
#!/usr/bin/env python3
"""
Auto-fix ESLint unused-imports errors for MD/MDX files.
Usage:
python autofix.py /tmp/eslint.json # path to eslint --format json output
If no path is provided, defaults to /tmp/eslint.json.
"""
from __future__ import annotations
import json
import re
import sys
from collections import defaultdict
from pathlib import Path
IMPORT_UNUSED_RULE = "unused-imports/no-unused-imports"
DEFAULT_ESLINT_JSON = Path("/tmp/eslint.json")
def load_unused_map(eslint_json: Path) -> dict[Path, set[str]]:
"""Return mapping of file -> unused import names from ESLint JSON output."""
data = json.loads(eslint_json.read_text())
unused_by_file: dict[Path, set[str]] = defaultdict(set)
for result in data:
file_path = Path(result["filePath"])
for msg in result.get("messages", []):
if msg.get("ruleId") != IMPORT_UNUSED_RULE:
continue
match = re.search(r"'([^']+)'", msg.get("message", ""))
if match:
unused_by_file[file_path].add(match.group(1))
return unused_by_file
def clean_imports(text: str, unused: set[str]) -> str:
"""Remove unused names from import lines; drop empty imports."""
lines = text.splitlines()
new_lines: list[str] = []
default_pat = re.compile(
r"^\s*import\s+([A-Za-z0-9_]+)(\s*,\s*\{[^}]*\})?\s+from\s+[^;]+;?\s*$"
)
named_pat = re.compile(r"^\s*import\s*\{([^}]*)\}\s*from\s*[^;]+;?\s*$")
in_fence = False
for line in lines:
stripped = line.lstrip()
# Respect fenced code blocks (``` or ~~~); leave imports inside untouched.
if stripped.startswith("```") or stripped.startswith("~~~"):
in_fence = not in_fence
new_lines.append(line)
continue
if in_fence:
new_lines.append(line)
continue
if not stripped.startswith("import "):
new_lines.append(line)
continue
ori_line = line
def keep_named(named_block: str) -> list[str]:
names = [n.strip() for n in named_block.split(",") if n.strip()]
return [n for n in names if n not in unused]
m = default_pat.match(line)
if m:
default_name = m.group(1)
named_block = m.group(2)
default_used = default_name not in unused
named_kept: list[str] = []
if named_block:
inner = named_block[named_block.find("{") + 1 : named_block.rfind("}")]
named_kept = keep_named(inner)
if default_used or named_kept:
parts: list[str] = []
if default_used:
parts.append(default_name)
if named_kept:
parts.append("{ " + ", ".join(named_kept) + " }")
newline = f"import {', '.join(parts)} from" + line.split(" from", 1)[1]
new_lines.append(newline)
# If nothing is kept, drop the import line entirely
continue
m = named_pat.match(line)
if m:
kept = keep_named(m.group(1))
if kept:
newline = f"import {{ {', '.join(kept)} }} from" + line.split(" from", 1)[1]
new_lines.append(newline)
continue
# Non-standard import line: leave it unchanged
new_lines.append(ori_line)
return "\n".join(new_lines) + ("\n" if text.endswith("\n") else "")
def apply_fixes(unused_by_file: dict[Path, set[str]]) -> int:
changed = 0
for file_path, unused_names in unused_by_file.items():
if not file_path.exists():
print(f"skip missing file: {file_path}")
continue
original = file_path.read_text()
updated = clean_imports(original, unused_names)
if updated != original:
file_path.write_text(updated)
changed += 1
print(f"cleaned {file_path}")
return changed
def main(argv: list[str]) -> int:
eslint_json = Path(argv[1]) if len(argv) > 1 else DEFAULT_ESLINT_JSON
if not eslint_json.exists():
print(f"ESLint output not found at {eslint_json}", file=sys.stderr)
return 1
unused_by_file = load_unused_map(eslint_json)
if not unused_by_file:
print("No unused-imports violations found.")
return 0
changed = apply_fixes(unused_by_file)
print(f"Updated {changed} file(s).")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
Member
Author
|
/review Check typos. No need to check anything else. ✅ Review completed. Review OutputExcellent! No common typos found. Based on my comprehensive review:
SummaryNo issues found I've thoroughly reviewed the PR for typos by:
All spelling and grammar in the PR appears to be correct. The changes consist primarily of:
No typos were detected in any of the added content. |
harupy
commented
Nov 24, 2025
Comment on lines
+50
to
+55
| 'unused-imports/no-unused-imports': 'error', | ||
| // These React rules prevent component imports from being flagged as unused. | ||
| // Required when using eslint-plugin-unused-imports with JSX/React code. | ||
| // https://www.npmjs.com/package/eslint-plugin-unused-imports | ||
| 'react/jsx-uses-vars': 'error', | ||
| 'react/jsx-uses-react': 'error', |
Member
Author
There was a problem hiding this comment.
it's a bit strange that plugins affect each other but we indeed need this.
Signed-off-by: harupy <17039389+harupy@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes are proposed in this pull request?
Add ESLint configuration to detect unused imports in MDX documentation files and remove all existing unused imports.
Changes:
eslint-plugin-unused-importsandeslint-plugin-reactunused-imports/no-unused-importsruleHow is this PR tested?
npm run eslintshows 0 errors)Does this PR require documentation update?
Release Notes
Is this a user-facing change?
What component(s), interfaces, languages, and integrations does this PR affect?
Components
area/docs: MLflow documentation pagesHow should the PR be classified in the release notes? Choose one:
rn/none- No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" sectionShould this PR be included in the next patch release?