NEON pro JavaScript

Balíček @nette/neon čte a zapisuje NEON v JavaScriptu a TypeScriptu, v Node i v prohlížeči. Díky němu:

  • dekódujete konfigurační soubor do hodnot JavaScriptu a hodnoty zakódujete zpět do NEONu
  • upravíte soubor tak, že každý komentář a mezera, na které nesáhnete, zůstanou na svém místě
  • zkontrolujete soubory pomocí neon-lint v editoru, v CI nebo po AI agentovi

Instalace

npm install @nette/neon

Jde o ES modul s typy pro TypeScript, pro Node 22 nebo novější či kterýkoliv současný prohlížeč, a nemá žádné závislosti. API Node používá jen příkaz neon-lint.

Dekódování a kódování

Funkce decode() převede NEON na hodnoty JavaScriptu a encode() je převede zpět:

import { decode, encode } from '@nette/neon';

decode('roles: [guest, admin]');           // { roles: ['guest', 'admin'] }
encode({ roles: ['guest', 'admin'] });     // '{roles: [guest, admin]}'
encode({ roles: ['guest', 'admin'] }, true); // 'roles:\n\t- guest\n\t- admin\n\n'

Knihovna sama soubory nečte, soubor tedy načte Node:

import { readFileSync } from 'node:fs';

const config = decode(readFileSync('config.neon', 'utf8'));

Místo true může být druhým argumentem encode() funkce, která u každého pole rozhodne, zda ho zapsat jako blok. Dostane pole a seznam klíčů, které k němu vedou:

encode(config, (value, path) => !path.length); // bloky jen v první úrovni

Hodnoty formátu se převedou na tyto hodnoty JavaScriptu:

NEON JavaScript
null, true, yes … null, boolean
celé číslo number; nad 253 bigint
desetinné číslo number
řetězec string
datum NeonDate
entita, řetěz Entity
sekvence Array
mapování obyčejný objekt

NeonDate drží datum tak, jak bylo zapsané, včetně chybějící časové zóny; toDate(zone) ho převede na Date. Mapování je obyčejný objekt jen s vlastními vlastnostmi, takže klíč jako __proto__ je prostě klíč.

Dvě věci plynou ze samotného JavaScriptu. Desetinné číslo bez desetinné části, třeba 1.0, je stejné number jako 1 a zapíše se zpět jako 1. A objekt uvádí klíče, které vypadají jako celá čísla, první a vzestupně; syntaktický strom pořadí ze souboru zachovává.

Chyby

Když vstup není platný NEON, decode() a parse() vyhodí NeonError. Jeho vlastnost position obsahuje řádek a sloupec problému:

import { decode, NeonError } from '@nette/neon';

try {
	decode('database:\n\thost: localhost\n\thost: db');
} catch (e) {
	if (e instanceof NeonError) {
		e.message;            // "Duplicated key 'host'"
		e.position.line;      // 3
	}
}

Úpravy souborů

Funkce parse() vrátí syntaktický strom souboru a přes něj soubor změníte tak, že jeho zbytek zůstane, jak ho autor napsal:

import { readFileSync, writeFileSync } from 'node:fs';
import { parse } from '@nette/neon';

const doc = parse(readFileSync('config.neon', 'utf8'));
doc.setValue(['database', 'host'], 'db.example.com');
writeFileSync('config.neon', doc.print());

Jak položky měnit, přidávat, odebírat a přejmenovávat a jak se vyznat ve stromu, popisuje stránka Úpravy souborů.

Kontrola souborů: neon-lint

Balíček obsahuje příkaz neon-lint. Předáte mu soubory nebo adresáře a on vypíše jeden řádek pro každý problém:

npx neon-lint config/
config/local.neon:3:2: Duplicated key 'user'

Příkaz, jeho volby i návratové kódy jsou stejné jako v knihovně pro PHP. Bez instalace balíčku ho spustíte jako npx --package=@nette/neon neon-lint config/.

AI agent, který ho spustí po každé změně souboru .neon, se o rozbitém souboru dozví hned. Návod pro AI agenty ukazuje, jak ho nastavit jako hook, a funguje bez ohledu na jazyk projektu.

Další příklady, každý se skutečným výstupem, najdete v repozitáři knihovny.