Metadata-Version: 2.4
Name: check-json5
Version: 1.3.0
Summary: pre-commit hook to check json files for syntax parseable as json5 (with comments)
Author-email: Simon van Lierde <30519500-simonvanlierde@users.noreply.gitlab.com>
License-Expression: MIT
Project-URL: Changelog, https://github.com/simonvanlierde/check-json5/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/simonvanlierde/check-json5/issues
Project-URL: Source, https://github.com/simonvanlierde/check-json5
Keywords: json,json5,jsonc,lint,pre-commit
Classifier: Environment :: Console
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: json5>=0.15.0
Dynamic: license-file

# check-json5

[![CI](https://github.com/simonvanlierde/check-json5/actions/workflows/ci.yml/badge.svg)](https://github.com/simonvanlierde/check-json5/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/simonvanlierde/check-json5/branch/main/graph/badge.svg)](https://codecov.io/gh/simonvanlierde/check-json5)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)

A [pre-commit](https://pre-commit.com/) hook that checks whether `.json` files parse as
[JSON5](https://json5.org/) (JSON with comments, trailing commas, and more). It replaces the
standard `check-json` hook, accepts JSON5 too, and keeps the speed of the C parser on ordinary
JSON.

## Features

- Accepts JSON5 syntax (comments, trailing commas) that the standard `check-json` hook rejects.
- Every failure names its place, as `file:line:col: message`, so editors and terminals link
  straight to it. A duplicate key also gets its path, and every repeat in a file is listed:

  ```text
  config.json:4:5: duplicate key $.server.port
  config.json:9:3: duplicate key $.plugins[2].name
  ```

- On GitHub Actions, each failure is also an annotation on the line of the pull request diff.
- `--strict GLOB` holds chosen files to strict JSON (no comments, no `NaN`), for files that a
  JSON parser reads at runtime, such as `package.json`.
- Fast path: strict JSON is parsed with the C-speed stdlib `json` parser, falling back to the
  pure-Python `json5` parser only when a file actually uses JSON5 features (~500× faster on large
  strict-JSON files; see [Performance](#performance)).
- Accepts `NaN` and `Infinity` (both parsers allow them) unless `--strict` covers the file.

## Links

- 🐙 [GitHub (main)](https://github.com/simonvanlierde/check-json5)
- 🦊 [GitLab (mirror)](https://gitlab.com/simonvanlierde/check-json5)

## Usage

In `.pre-commit-config.yaml`, under `repos:`, add:

```yaml
- repo: https://github.com/simonvanlierde/check-json5
  rev: v1.3.0
  hooks:
    - id: check-json5
```

Remove the original `check-json` hook if it is already included. A file passes when it parses as
JSON5 and has no duplicate keys. The hook picks files by extension (`.json`, `.jsonc`, `.json5`,
`.geojson`, `.jsonld`), while `check-json` picks them by type, so files such as `.babelrc` or
`.ipynb` are no longer checked after the swap. Add them with `files:` if you need them.

### Strict files

Some JSON files are read by a strict parser at runtime: a comment in `package.json` passes a JSON5
check and then breaks `npm`. Name those files with `--strict`. Each pattern matches the file name
at any depth, or the whole path when it contains a `/`. `*` also matches across `/`. A leading `!`
exempts files, and the last matching pattern wins:

```yaml
    - id: check-json5
      args: [--strict, "*.json", --strict, "!tsconfig*.json", --strict, "!.vscode/*"]
```

### Without pre-commit

The hook is also a command on PyPI:

```bash
uvx check-json5 --strict package.json config/*.json
```

## Introduction

JSON5 is similar to JSON but permits comments, trailing commas, and more. It resembles "JSONC" (JSON
with Comments) but, unlike JSONC, has an actual specification. Many config files in the wild are
really JSON5, so the standard `check-json` hook, which rejects comments outright, flags them as
invalid. This hook accepts them.

Strict-JSON files are parsed with the fast built-in [`json`](https://docs.python.org/3/library/json.html)
library; only files that use JSON5 features fall back to the pure-Python
[`json5`](https://pypi.org/project/json5/) library.

## Performance

The pure-Python `json5` parser is slow on large files. Since most `.json` files are strict JSON, the
hook tries the C-speed stdlib `json` parser first and only falls back to `json5` when a file uses
comments or trailing commas. Duplicate-key detection works on both paths.

Time for one hook call per file (strict JSON, taking the fast path), best of 10 runs of
`uv run python scripts/bench.py` on 2026-10-11 (Intel i9-14900KF, Linux, Python 3.13.7, json5 0.15.0,
load average 1.5):

| File size | Before (`json5` only) | After (fast path) | Speedup |
| --------- | --------------------- | ----------------- | ------- |
| 1 KB      | 2.5 ms                | 0.02 ms           | ~100×   |
| 47 KB     | 123 ms                | 0.19 ms           | ~660×   |
| 483 KB    | 1.3 s                 | 1.8 ms            | ~750×   |
| 2 MB      | 5.4 s                 | 7.7 ms            | ~700×   |

The script generates its fixture files in a temporary directory, so nothing large is committed.
Timings vary between runs by up to 2× on a loaded machine. The 1 KB fast-path time is close to
the timer's resolution, so its speed-up ranged from ~80× to ~100× across runs.

To time whole hook runs instead, run `bash scripts/bench.sh` (needs
[hyperfine](https://github.com/sharkdp/hyperfine)). It times the hook against the `json5` parser
on 50 KB, 500 KB and 2 MB files and prints one table; `--quick` times the 50 KB file once. Each
run includes about 20 ms of Python startup, so its speed-ups are smaller than the ones above.

Files that use JSON5 syntax still go through the `json5` parser and are unchanged. For tiny config
files the absolute saving is negligible next to Python's own startup cost; the win matters on large
JSON files.

## Development

The project uses [uv](https://github.com/astral-sh/uv) for dependency management, `pytest` for
tests, `pre-commit` for local checks, and GitHub Actions for CI with coverage reported to Codecov.

```bash
uv sync                   # install dependencies
uv run pytest             # run the test suite
uv run pytest --cov       # tests with coverage
uv run pre-commit run -a  # run the configured hooks locally
```

## Credits

Adapted by Simon van Lierde from the original [check-json5](https://gitlab.com/bmares/check-json5) by
Ben Mares, itself based on the
[pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks) repository by Anthony Sottile and
contributors. This version switches dependency management from Poetry to uv and adds the fast-path
parser and benchmark above.

## License

[MIT](LICENSE), consistent with the upstream projects it is based on
([Ben Mares](https://gitlab.com/bmares/check-json5/-/blob/main/LICENSE),
[pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks/blob/main/LICENSE)).
