Skip to content

Commit bc319d6

Browse files
committed
feat: initial commit
1 parent 386c551 commit bc319d6

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

.pre-commit-config.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
repos:
2+
- repo: https://github.com/asottile/reorder_python_imports
3+
rev: v2.5.0
4+
hooks:
5+
- id: reorder-python-imports
6+
- repo: https://github.com/psf/black
7+
rev: 21.5b2
8+
hooks:
9+
- id: black
10+
language_version: python3
11+
- repo: https://github.com/pre-commit/pre-commit-hooks
12+
rev: v4.0.1
13+
hooks:
14+
- id: fix-byte-order-marker
15+
- id: trailing-whitespace
16+
- id: end-of-file-fixer
17+
- repo: https://github.com/pre-commit/mirrors-prettier
18+
rev: "v2.3.0"
19+
hooks:
20+
- id: prettier
21+
types: [file]
22+
files: \.(js|jsx|ts|tsx|yaml|yml|json|json5|md)$
23+
additional_dependencies:
24+
- prettier
25+
- "@whtsky/prettier-config"

.pre-commit-hooks.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- id: pretty-format-json5
2+
name: Pretty Format JSON5
3+
description: This hook checks that all your JSON5 files are pretty.
4+
entry: pretty-format-json5
5+
language: python
6+
files: \.json5$

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
# pre-commit-pretty-format-json5
2+
23
A pre-commit hook that checks that all your JSON5 files are pretty.
4+
5+
## Usage
6+
7+
```yaml
8+
- repo: https://github.com/whtsky/pre-commit-pretty-format-json5
9+
rev: "1.0.0"
10+
hooks:
11+
- id: pretty-format-json5
12+
```
13+
14+
commandline options:
15+
16+
- `--no-autofix` - Don't automatically format json files
17+
- `--indent ...` - Control the indentation (either a number for a number of spaces or a string of whitespace). Defaults to 2 spaces.
18+
- `--ensure-ascii` converte unicode characters to escape sequences
19+
- `--no-sort-keys` - when autofixing, retain the original key ordering (instead of sorting the keys)
20+
- `--top-keys comma,separated,keys` - Keys to keep at the top of mappings.

pretty_format_json5.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import argparse
2+
import sys
3+
from difflib import unified_diff
4+
from typing import List
5+
from typing import Mapping
6+
from typing import Optional
7+
from typing import Sequence
8+
from typing import Tuple
9+
from typing import Union
10+
11+
import json5
12+
13+
14+
# Forked from https://github.com/pre-commit/pre-commit-hooks/blob/f48244a8055c1d51955ee6312d8942db325672cf/pre_commit_hooks/check_json.py
15+
16+
17+
def _get_pretty_format(
18+
contents: str,
19+
indent: str,
20+
ensure_ascii: bool = True,
21+
sort_keys: bool = True,
22+
top_keys: Sequence[str] = (),
23+
) -> str:
24+
def pairs_first(pairs: Sequence[Tuple[str, str]]) -> Mapping[str, str]:
25+
before = [pair for pair in pairs if pair[0] in top_keys]
26+
before = sorted(before, key=lambda x: top_keys.index(x[0]))
27+
after = [pair for pair in pairs if pair[0] not in top_keys]
28+
if sort_keys:
29+
after.sort()
30+
return dict(before + after)
31+
32+
json_pretty = json5.dumps(
33+
json5.loads(contents, object_pairs_hook=pairs_first),
34+
indent=indent,
35+
ensure_ascii=ensure_ascii,
36+
)
37+
return f"{json_pretty}\n"
38+
39+
40+
def _autofix(filename: str, new_contents: str) -> None:
41+
print(f"Fixing file {filename}")
42+
with open(filename, "w", encoding="UTF-8") as f:
43+
f.write(new_contents)
44+
45+
46+
def parse_num_to_int(s: str) -> Union[int, str]:
47+
"""Convert string numbers to int, leaving strings as is."""
48+
try:
49+
return int(s)
50+
except ValueError:
51+
return s
52+
53+
54+
def parse_topkeys(s: str) -> List[str]:
55+
return s.split(",")
56+
57+
58+
def get_diff(source: str, target: str, file: str) -> str:
59+
source_lines = source.splitlines(True)
60+
target_lines = target.splitlines(True)
61+
diff = unified_diff(source_lines, target_lines, fromfile=file, tofile=file)
62+
return "".join(diff)
63+
64+
65+
def main(argv: Optional[Sequence[str]] = None) -> int:
66+
parser = argparse.ArgumentParser()
67+
parser.add_argument(
68+
"--no-autofix",
69+
action="store_true",
70+
dest="no_autofix",
71+
default=False,
72+
help="Don't automatically fixes encountered not-pretty-formatted files",
73+
)
74+
parser.add_argument(
75+
"--indent",
76+
type=parse_num_to_int,
77+
default="2",
78+
help=(
79+
"The number of indent spaces or a string to be used as delimiter"
80+
' for indentation level e.g. 4 or "\t" (Default: 2)'
81+
),
82+
)
83+
parser.add_argument(
84+
"--ensure-ascii",
85+
action="store_true",
86+
dest="ensure_ascii",
87+
default=False,
88+
help=("Convert non-ASCII characters to Unicode escape sequences " "(\\uXXXX)"),
89+
)
90+
parser.add_argument(
91+
"--no-sort-keys",
92+
action="store_true",
93+
dest="no_sort_keys",
94+
default=False,
95+
help="Keep JSON nodes in the same order",
96+
)
97+
parser.add_argument(
98+
"--top-keys",
99+
type=parse_topkeys,
100+
dest="top_keys",
101+
default=[],
102+
help="Ordered list of keys to keep at the top of JSON hashes",
103+
)
104+
parser.add_argument("filenames", nargs="*", help="Filenames to fix")
105+
args = parser.parse_args(argv)
106+
107+
status = 0
108+
109+
for json_file in args.filenames:
110+
with open(json_file, encoding="UTF-8") as f:
111+
contents = f.read()
112+
113+
try:
114+
pretty_contents = _get_pretty_format(
115+
contents,
116+
args.indent,
117+
ensure_ascii=args.ensure_ascii,
118+
sort_keys=not args.no_sort_keys,
119+
top_keys=args.top_keys,
120+
)
121+
except ValueError:
122+
print(
123+
f"Input File {json_file} is not a valid JSON, consider using "
124+
f"check-json",
125+
)
126+
return 1
127+
128+
if contents != pretty_contents:
129+
if not args.no_autofix:
130+
_autofix(json_file, pretty_contents)
131+
else:
132+
diff_output = get_diff(contents, pretty_contents, json_file)
133+
sys.stdout.buffer.write(diff_output.encode())
134+
135+
status = 1
136+
137+
return status
138+
139+
140+
if __name__ == "__main__":
141+
sys.exit(main())

setup.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from setuptools import setup
2+
3+
4+
setup(
5+
name="pretty_format_json5",
6+
version="0.0.1",
7+
py_modules=["pretty_format_json5"],
8+
install_requires=["json5==0.9.5"],
9+
entry_points={
10+
"console_scripts": ["pretty-format-json5=pretty_format_json5:main"],
11+
},
12+
)

0 commit comments

Comments
 (0)