Created
December 6, 2025 06:44
-
-
Save skorotkiewicz/359d3b48d71cd085886a494a2c81208b to your computer and use it in GitHub Desktop.
The tool stores comments in a .jsonc file and maintains clean JSON in the original file.
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
| #!/usr/bin/env python3 | |
| """ | |
| JSON with Comments editor - Edit JSON files with comments | |
| Usage: | |
| ./jsonc <file> - Edit JSON with comments in your editor | |
| cat <file> - View clean JSON without comments | |
| The tool stores comments in a .jsonc file and maintains clean JSON in the original file. | |
| """ | |
| import sys | |
| import json | |
| import os | |
| import subprocess | |
| import tempfile | |
| import re | |
| def strip_comments(text): | |
| """Remove // and /* */ style comments from JSON text""" | |
| # Remove /* */ comments | |
| text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) | |
| # Remove // comments | |
| lines = [] | |
| for line in text.split('\n'): | |
| in_string = False | |
| escape = False | |
| for i, char in enumerate(line): | |
| if escape: | |
| escape = False | |
| continue | |
| if char == '\\': | |
| escape = True | |
| continue | |
| if char == '"': | |
| in_string = not in_string | |
| if char == '/' and i + 1 < len(line) and line[i + 1] == '/' and not in_string: | |
| line = line[:i] | |
| break | |
| lines.append(line) | |
| return '\n'.join(lines) | |
| def edit_json(json_path): | |
| """Edit JSON with comments""" | |
| base = os.path.splitext(json_path)[0] | |
| jsonc_path = base + '.jsonc' | |
| # Determine what content to edit | |
| if os.path.exists(jsonc_path): | |
| # Edit existing .jsonc file | |
| with open(jsonc_path, 'r') as f: | |
| content = f.read() | |
| elif os.path.exists(json_path): | |
| # Create .jsonc from existing .json | |
| with open(json_path, 'r') as f: | |
| content = f.read() | |
| print(f"Creating {jsonc_path} from {json_path}") | |
| else: | |
| # Create new file with template | |
| content = """{ | |
| // Add your configuration here | |
| "example": "value" | |
| }""" | |
| print(f"Creating new files: {json_path} and {jsonc_path}") | |
| # Open in editor | |
| editor = os.environ.get('EDITOR', 'nano') | |
| with tempfile.NamedTemporaryFile(mode='w', suffix='.jsonc', delete=False) as tf: | |
| tf.write(content) | |
| temp_path = tf.name | |
| try: | |
| subprocess.run([editor, temp_path], check=True) | |
| # Read edited content | |
| with open(temp_path, 'r') as f: | |
| new_content = f.read() | |
| # Validate JSON | |
| try: | |
| data = json.loads(strip_comments(new_content)) | |
| except json.JSONDecodeError as e: | |
| print(f"Error: Invalid JSON: {e}", file=sys.stderr) | |
| sys.exit(1) | |
| # Save .jsonc with comments | |
| with open(jsonc_path, 'w') as f: | |
| f.write(new_content) | |
| # Save .json without comments (pretty-printed) | |
| with open(json_path, 'w') as f: | |
| json.dump(data, f, indent=2) | |
| print(f"Saved {json_path} (clean) and {jsonc_path} (with comments)") | |
| finally: | |
| os.unlink(temp_path) | |
| def main(): | |
| if len(sys.argv) < 2: | |
| print(__doc__) | |
| sys.exit(1) | |
| edit_json(sys.argv[1]) | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment