@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/bin/neon-lint.js +4 -0
- package/dist/builder.d.ts +28 -0
- package/dist/builder.js +76 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +172 -0
- package/dist/dumper.d.ts +5 -0
- package/dist/dumper.js +75 -0
- package/dist/editing.d.ts +41 -0
- package/dist/editing.js +1188 -0
- package/dist/encoder.d.ts +16 -0
- package/dist/encoder.js +292 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +14 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -0
- package/dist/internal.d.ts +11 -0
- package/dist/internal.js +32 -0
- package/dist/layout.d.ts +14 -0
- package/dist/layout.js +105 -0
- package/dist/lexer.d.ts +8 -0
- package/dist/lexer.js +59 -0
- package/dist/neon.d.ts +19 -0
- package/dist/neon.js +22 -0
- package/dist/nodes.d.ts +227 -0
- package/dist/nodes.js +635 -0
- package/dist/parser.d.ts +3 -0
- package/dist/parser.js +404 -0
- package/dist/position.d.ts +8 -0
- package/dist/position.js +40 -0
- package/dist/printer.d.ts +8 -0
- package/dist/printer.js +40 -0
- package/dist/scanner.d.ts +30 -0
- package/dist/scanner.js +193 -0
- package/dist/token.d.ts +67 -0
- package/dist/token.js +309 -0
- package/dist/traverser.d.ts +31 -0
- package/dist/traverser.js +96 -0
- package/dist/utf8.d.ts +2 -0
- package/dist/utf8.js +44 -0
- package/dist/values.d.ts +75 -0
- package/dist/values.js +316 -0
- package/license.md +21 -0
- package/package.json +60 -6
- package/readme.md +242 -0
- package/README.md +0 -3
package/readme.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
NEON for JavaScript
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.com/package/@nette/neon)
|
|
5
|
+
[](https://github.com/nette/neon-js/actions)
|
|
6
|
+
[](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