@cldmv/eslint-plugin-jsonv
ESLint plugin for validating .jsonv files using the @cldmv/jsonv parser.
✨ What's New
Latest: v1.1.2 (November 2026)
- Tooling release, no plugin changes — the vitest config now uses
devCondition()from@cldmv/configs, so tests really run againstsrc/through theeslint-plugin-jsonv-devexport condition, and the type scripts are split intotypes:checkandtypes:build(#92, #94). No rule, language or option changed. - View full v1.1.2 Changelog
Recent Releases
- v1.1.1 (November 2026) —
engines.nodeback to the runtime range^20.19.0 || ^22.13.0 || >=24; the 22.22.0 development floor moves todevEngines(#88) (Changelog) - v1.1.0 (November 2026) — plugin moved into
src/, built with tsup, with arequire()entry and aneslint-plugin-jsonv-devexport condition;engines.nodenarrowed to>=22.22.0(#82, #83) (Changelog) - v1.0.17 (October 2026) — development dependencies only:
eslint10.12.0, vitest 5.0.3 and@cldmv/vitest-runner1.5.3; no plugin code changed (#59, #60, #62, #64) (Changelog) - v1.0.16 (October 2026) — header tooling on
@cldmv/fix-headers2.2.0 and@cldmv/configs1.2.4; no plugin code or runtime dependency changed (#54, #57) (Changelog)
📚 For complete version history, see docs/changelog/ and the GitHub Releases.
Features
- Syntax Validation: Validates jsonv syntax using the actual jsonv parser
- ES2011-2025 Support: All JSON5, ES2015, ES2020, ES2021 features supported
- Internal References: Validates bare identifiers, template interpolation, nested property access
- BigInt Support: Validates BigInt literals and numeric separators
- Accurate Error Reporting: Parse errors include line/column information
- Configurable Parsing: Target ES year, parse mode, strict BigInt and octal checks, and internal reference resolution
Installation
npm install --save-dev @cldmv/eslint-plugin-jsonv
Requirements: Node ^20.19.0 || ^22.13.0 || >=24 (the range the plugin's @eslint/core and @eslint/plugin-kit dependencies support) and ESLint ^9.13.0 || ^10.0.0 (the first ESLint release with defaultLanguageOptions support for plugin languages). require() needs Node.js ^20.19.0 or >=22.13.0, which that range already covers.
Developing this package needs Node.js 22.22.0 or later (devEngines).
Note: This plugin requires @cldmv/jsonv as a peer dependency.
npm install @cldmv/jsonv
Building the Plugin
The source lives in src/: src/index.mjs is the plugin and src/index.cjs is the CommonJS entry. To build it:
npm install
npm run build
This will:
- Bundle
src/index.mjsintodist/index.mjswith tsup - Copy
src/index.cjsunchanged todist/index.cjs - Generate type definitions into
dist/index.d.mts
dist/ is not committed; npm pack and npm publish rebuild it.
Running from source
The package's "." export has an eslint-plugin-jsonv-dev condition that points at src/ instead of dist/. In a checkout, import or require of @cldmv/eslint-plugin-jsonv loads dist/ (so build first) unless the condition is set:
node --conditions=eslint-plugin-jsonv-dev app.mjs
# or once per shell
export NODE_OPTIONS=--conditions=eslint-plugin-jsonv-dev
The test suite sets the condition itself, so npm test runs against src/, then npm run test:dist builds and checks dist/.
Usage
ESLint Flat Config (eslint.config.mjs)
import jsonv from "@cldmv/eslint-plugin-jsonv";
export default [
{
files: ["**/*.jsonv"],
plugins: { jsonv },
language: "jsonv/jsonv",
extends: ["jsonv/recommended"]
}
];
ESLint Flat Config (eslint.config.cjs)
The package also has a CommonJS entry, so a CommonJS config can require() it. require() returns the plugin object itself, the same object as the ESM default export. This needs a Node.js release with require(esm) support (^20.19.0 or >=22.13.0), which the supported Node range already guarantees.
const jsonv = require("@cldmv/eslint-plugin-jsonv");
module.exports = [
{
files: ["**/*.jsonv"],
plugins: { jsonv },
language: "jsonv/jsonv",
extends: ["jsonv/recommended"]
}
];
Configuration Options
languageOptions are passed to the @cldmv/jsonv parser (both parseWithOptions(), which reports errors, and parseToAst(), which builds the AST rules see). Every option is optional; the values below are the defaults:
{
files: ["**/*.jsonv"],
plugins: { jsonv },
language: "jsonv/jsonv",
languageOptions: {
year: 2025, // Target ES year: which jsonv features are allowed
mode: "jsonv", // Parse mode: "jsonv", "json5" or "json"
strictBigInt: false, // true: an unsafe integer needs the `n` suffix
strictOctal: false, // true: legacy `0755` octals are errors (use `0o755`)
allowInternalReferences: true // false: references are not resolved or checked
}
}
| Option | Type | Allowed values | Default | Meaning |
|---|---|---|---|---|
year | number | 2011, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 | 2025 | The target ES year. A feature introduced after it is an error: binary/octal literals and template literals need 2015, BigInt needs 2020, numeric separators need 2021. |
mode | string | "jsonv", "json5", "json" | "jsonv" | The parse mode. "jsonv" allows every jsonv feature of the selected year, "json5" is meant for JSON5 only and "json" for strict JSON. The plugin passes the mode through unchanged, so it enforces exactly what @cldmv/jsonv enforces: in @cldmv/jsonv 1.1.0, "json" rejects comments, and the other JSON and JSON5 restrictions are not enforced yet. |
strictBigInt | boolean | true, false | false | When true, an integer outside the safe range (±9007199254740991) without an n suffix is an error. When false, it is read as a BigInt. |
strictOctal | boolean | true, false | false | When true, a legacy octal literal such as 0755 is an error; 0o755 is still allowed. |
allowInternalReferences | boolean | true, false | true | When true, internal references (backup: port, server.port, `${host}`) are resolved, and an undefined or circular reference is an error. When false, they are left unresolved and are not reported. |
Any other key, or a value outside the allowed ones, is a configuration error (a TypeError naming the option).
Three @cldmv/jsonv parse options are deliberately not accepted:
reviver: a reviver only transforms the evaluated value, and linting does not use that value.preserveComments: the AST always carries the comments, so inlineeslint-disablecomments and rules can see them.tolerant: the plugin controls how parse errors are collected.
Inline Configuration
The usual ESLint comments work inside .jsonv files: // eslint-disable, /* eslint-disable <rule> */, /* eslint-enable */, // eslint-disable-line, // eslint-disable-next-line, and /* eslint <rule>: "off" */ rule configuration.
Writing Rules
The jsonv/jsonv language exposes the document as an AST built from @cldmv/jsonv's parseToAst(). Every node has a loc (1-based lines and columns) and a range, so reports on a node land on that node, and rules can use node types and selectors:
| Node type | Children | Notes |
|---|---|---|
Program | body | The document; body is the root value. comments and tokens hang off it. |
ObjectExpression | properties | { ... } |
Property | key, value | key is a Literal (quoted or numeric key) or an Identifier (unquoted key). |
ArrayExpression | elements | [ ... ] |
Literal | — | Strings, numbers, BigInt (bigint holds the digits), booleans, null, Infinity, NaN; raw is the source text. |
Identifier | — | An unquoted key, or an internal reference such as backup: port. |
MemberExpression | object, property | A dotted internal reference such as server.port. |
TemplateLiteral | quasis, expressions | A backtick string with ${...} interpolation (a plain backtick string is a Literal). |
TemplateElement | — | A literal segment of a template. |
// Report every string value containing "TODO", on the string itself
create(context) {
return {
"Property > Literal.value"(node) {
if (typeof node.value === "string" && node.value.includes("TODO")) {
context.report({ node, message: "Unexpected TODO." });
}
}
};
}
Supported Features
ES2011 (JSON5 Base)
- Single-line (
//) and multi-line (/* */) comments - Trailing commas in objects and arrays
- Unquoted object keys
- Single-quoted strings
- Hexadecimal number literals (
0xFF) - Leading/trailing decimal points (
.5,5.) - Explicit positive sign (
+5) Infinity,-Infinity,NaN- Multi-line strings with backslash continuation
- Internal references via bare identifiers:
{ port: 8080, backup: port }
ES2015 (ES6)
- Binary literals (
0b1010) - Octal literals (
0o755, also legacy0755) - Template literals (backtick strings)
- Template interpolation for internal refs:
url: `http://${host}:${port}`
ES2020
- BigInt literals (
9007199254740992n) - BigInt in hex/binary/octal formats
ES2021
- Numeric separators (
1_000_000,0xFF_AA,0b1111_0000)
Example
config.jsonv:
{
// Server configuration with internal references
host: "localhost",
port: 8080,
// Template interpolation
url: `http://${host}:${port}`,
// ES2021 numeric separators
maxConnections: 1_000_000,
// ES2020 BigInt
userId: 9007199254740993n,
// ES2015 binary flags
permissions: 0b1111_0000,
// Nested reference
monitoring: {
healthCheck: url
}
}
License
Apache-2.0 © Shinrai / CLDMV