@nette/neon 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md ADDED
@@ -0,0 +1,242 @@
1
+ NEON for JavaScript
2
+ ===================
3
+
4
+ [![npm](https://img.shields.io/npm/v/@nette/neon.svg)](https://www.npmjs.com/package/@nette/neon)
5
+ [![Tests](https://github.com/nette/neon-js/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/nette/neon-js/actions)
6
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nette/neon-js/blob/master/license.md)
7
+
8
+  <!---->
9
+
10
+ <h3>
11
+
12
+ ✅ A NEON parser that keeps [every comment and space](#the-tree-nodes-slots-tokens-trivia)<br>
13
+ ✅ [Edit a configuration file](#format-preserving-editing) and the diff is only your change<br>
14
+ ✅ [`neon-lint`](#for-editors-and-ai-agents) for editors, CI and AI agents<br>
15
+ ✅ [No dependencies](#installation), runs in Node and in the browser
16
+
17
+ </h3>
18
+
19
+  <!---->
20
+
21
+ **NEON parser, encoder and lossless editor for JavaScript and TypeScript.** NEON is the
22
+ human-friendly configuration format of the Nette framework, a YAML alternative with entities.
23
+ This library parses it into a concrete syntax tree in which every character of the file has
24
+ its place, so printing the tree gives the file back byte for byte:
25
+
26
+ ```ts
27
+ const doc = parse(text);
28
+ doc.print() === text; // true, for any input NEON accepts
29
+ ```
30
+
31
+ That is what makes editing safe. Change a value, add a key, remove an item, and the rest of the
32
+ file stays as its author wrote it:
33
+
34
+ ```ts
35
+ doc.setValue(['database', 'host'], 'db.example.com');
36
+ ```
37
+
38
+ ```
39
+ - host: localhost # the server
40
+ + host: db.example.com # the server
41
+ ```
42
+
43
+ On that you build configuration editors and admin panels, installers that register an extension
44
+ in `config.neon`, migrations that rename a service, and AI agents that change configuration
45
+ without wrecking it.
46
+
47
+ The library comes from David Grudl, the author of [Nette](https://nette.org),
48
+ [Latte](https://latte.nette.org) and [Tracy](https://tracy.nette.org). NEON has been the
49
+ configuration format of Nette since 2011, read by every Nette application, and this
50
+ implementation follows the same specification as [nette/neon](https://github.com/nette/neon) for PHP.
51
+
52
+  <!---->
53
+
54
+ NEON in one minute
55
+ ==================
56
+
57
+ ```neon
58
+ # mappings and sequences, indented by tabs or spaces
59
+ database:
60
+ host: localhost
61
+ port: 3306
62
+
63
+ roles: [guest, member, admin] # inline arrays
64
+
65
+ # entities: a value with attributes, the way Nette writes services
66
+ mailer: App\Mailer(@smtp, timeout: 30)
67
+
68
+ note: '''
69
+ multiline strings
70
+ without escaping
71
+ '''
72
+ ```
73
+
74
+ JSON is a subset of NEON. The format is described at [ne-on.org](https://ne-on.org) and in the
75
+ [manual](https://doc.nette.org/en/neon/format).
76
+
77
+ ▶ Runnable examples: [examples/edge-cases](examples/edge-cases/)
78
+
79
+  <!---->
80
+
81
+ Why NEON, if YAML exists
82
+ ========================
83
+
84
+ YAML is widespread and its libraries are mature. NEON did not start as its replacement but as the
85
+ configuration format of Nette, and it has three things YAML does not have or has differently:
86
+
87
+ - **entities**: `Column(type: int, nullable: true)` is a value, and the configuration of the DI
88
+ container of Nette lives on it;
89
+ - **tabs** for indentation;
90
+ - **a smaller language**: no anchors, tags, directives or multi-document streams, and no flow
91
+ and block styles with different meanings.
92
+
93
+ Both read JSON.
94
+
95
+  <!---->
96
+
97
+ Format-preserving editing
98
+ =========================
99
+
100
+ Most YAML libraries, like js-yaml, drop comments and formatting when they write a file; those
101
+ that keep them, like the `yaml` package, document where they cannot. Here it is an invariant, not
102
+ an effort: every edit changes the tokens it edits, and the four guarantees of the specification
103
+ (the file reads back as the same tree, with the expected value, with the change only where it was
104
+ made) are checked after every operation of a large corpus of edits.
105
+
106
+ ```ts
107
+ import { CommentPolicy, parse } from '@nette/neon';
108
+
109
+ const doc = parse(text);
110
+ doc.setValue(['database', 'port'], 3307); // 0x0CEA stays hexadecimal: 0x0CEB
111
+ doc.setValue(['database', 'debug'], false); // yes becomes no, not false
112
+ doc.setValue(['timeout'], 30); // the same value: no change at all
113
+ doc.setValue(['parameters', 'admin'], 'a@b.cz'); // a new key, indented like its siblings
114
+ doc.addItem(['services'], 'App\\OrderFacade'); // a new item of a list
115
+ doc.removeItem(['database', 'driver']); // with the comment that describes it
116
+ doc.removeItem(['mail'], { comments: CommentPolicy.MoveToNextToken });
117
+ ```
118
+
119
+ A missing key in the middle of a path is an error, so a typo does not create a new section.
120
+ Removing the last item of a collection leaves it empty (`services: []`), because in a
121
+ configuration empty and missing often mean different things; an item marked `nullable` loses its
122
+ value instead.
123
+
124
+ ▶ Runnable examples: [examples/editing](examples/editing/), [examples/config-editor](examples/config-editor/)
125
+
126
+  <!---->
127
+
128
+ Installation
129
+ ============
130
+
131
+ ```shell
132
+ npm install @nette/neon
133
+ ```
134
+
135
+ ES module, TypeScript types included, Node 22 or newer, or any current browser. No dependencies.
136
+ The core uses no Node API; only the `neon-lint` command does.
137
+
138
+  <!---->
139
+
140
+ The tree: nodes, slots, tokens, trivia
141
+ ======================================
142
+
143
+ ```ts
144
+ const item = doc.findItem(['database', 'user']);
145
+ item.getValue(); // 'root'
146
+ item.getTrailingComments(); // the comment on its line
147
+ item.value.getFirstToken().text; // the text exactly as written
148
+ item.getPosition(); // { line, column, offset }, computed from the current tree
149
+ ```
150
+
151
+ Eight node classes (`DocumentNode`, `BlockArrayNode`, `InlineArrayNode`, `ArrayItemNode`,
152
+ `EntityNode`, `EntityChainNode`, `StringNode`, `LiteralNode`) with named slots, tokens with their
153
+ trivia (whitespace, line endings, comments), `find()`, `findAncestor()`, siblings, and
154
+ `Traverser` for a walk with replacements.
155
+
156
+ ▶ Runnable examples: [examples/tree](examples/tree/)
157
+
158
+  <!---->
159
+
160
+ Decoding and encoding
161
+ =====================
162
+
163
+ ```ts
164
+ import { decode, encode } from '@nette/neon';
165
+
166
+ decode('a: [1, 2]'); // { a: [1, 2] }
167
+ encode({ a: [1, 2] }); // '{a: [1, 2]}'
168
+ encode({ a: [1, 2] }, true); // 'a:\n\t- 1\n\t- 2\n\n'
169
+ encode({ a: [1, 2] }, (_, path) => !path.length); // 'a: [1, 2]\n', blocks for the first level only
170
+ encode(config, (value) => !Array.isArray(value)); // lists on one line
171
+ ```
172
+
173
+ | NEON | JavaScript |
174
+ |---|---|
175
+ | `null`, `true`, `yes` ... | `null`, `boolean` |
176
+ | integer | `number`, beyond 2^53 a `bigint` |
177
+ | float | `number` |
178
+ | string | `string` |
179
+ | date | `NeonDate` (components and offset as written; `toDate(zone)` for an instant) |
180
+ | entity, chain | `Entity` |
181
+ | list | `Array` |
182
+ | map | plain object with own properties, so `__proto__` is just a key |
183
+
184
+ Two differences from PHP follow from JavaScript: an integral float like `1.0` is a `number` like
185
+ `1` and is written as `1`, and a plain object orders integer-like keys first (the tree keeps the
186
+ order of the file).
187
+
188
+ ▶ Runnable examples: [examples/parsing](examples/parsing/), [examples/encoding](examples/encoding/)
189
+
190
+  <!---->
191
+
192
+ One format, one specification
193
+ =============================
194
+
195
+ The format, the value model, the tree and the semantics of editing are written down in a
196
+ language-neutral [specification](https://github.com/nette/neon-specification) with a conformance corpus,
197
+ and every implementation runs that corpus in its tests. The values and the error messages were
198
+ checked against nette/neon for PHP over 95,000 real-world files. The corpus is public; if you write
199
+ an implementation of NEON for another language, use it.
200
+
201
+  <!---->
202
+
203
+ For editors and AI agents
204
+ =========================
205
+
206
+ ```shell
207
+ npx neon-lint config/
208
+ ```
209
+
210
+ ```
211
+ config/local.neon:3:2: Duplicated key 'user'
212
+ ```
213
+
214
+ One line per finding, the exit code as the verdict, `--json` for tools.
215
+ Editor plugins exist for PhpStorm, VS Code, Sublime Text, Vim and Emacs.
216
+
217
+ For **AI agents**, a short hook gives the agent an error right after it saves a broken `.neon`
218
+ file, so it fixes it before it goes on. The hook runs after the edit; it does not
219
+ prevent a broken save, but it makes sure it does not go unnoticed. See
220
+ [docs/skills/agents.md](docs/skills/agents.md).
221
+
222
+ ▶ Runnable examples: [examples/linting](examples/linting/)
223
+
224
+  <!---->
225
+
226
+ Limits
227
+ ======
228
+
229
+ - No formatter: the library keeps your formatting, it does not impose one.
230
+ - No schema or validation of values.
231
+ - No error recovery: an invalid file is reported, not partially parsed.
232
+ - Positions are computed on request, from the current tree.
233
+
234
+  <!---->
235
+
236
+ Documentation and credits
237
+ =========================
238
+
239
+ The [examples](examples/) are the documentation of the API, each chapter with the real output of
240
+ its programs. The format itself is documented in the [Nette manual](https://doc.nette.org/en/neon).
241
+
242
+ Released under the [MIT license](license.md).
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.