@nette/neon 1.0.0 → 1.0.1
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/dist/editing.js +29 -13
- package/dist/parser.js +42 -8
- package/dist/values.d.ts +1 -1
- package/dist/values.js +1 -4
- package/package.json +2 -2
- package/readme.md +3 -2
package/dist/editing.js
CHANGED
|
@@ -34,12 +34,22 @@ export function setItemValue(item, value) {
|
|
|
34
34
|
else {
|
|
35
35
|
replaceValue(item, styledScalar(old, value) ?? Builder.value(value, placeOf(item, old, value)));
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
guardGrammar(item.parent);
|
|
38
38
|
}
|
|
39
39
|
/** Puts the node into the value slot or empties it, without the comparison and styling of setItemValue(). */
|
|
40
40
|
export function setItemNode(item, node) {
|
|
41
41
|
replaceValue(item, node);
|
|
42
|
-
|
|
42
|
+
guardGrammar(item.parent);
|
|
43
|
+
}
|
|
44
|
+
/** Repairs what the grammar would read differently in the array after an edit. */
|
|
45
|
+
function guardGrammar(array) {
|
|
46
|
+
if (array instanceof BlockArrayNode) {
|
|
47
|
+
guardEnd(array);
|
|
48
|
+
guardDashSubblocks(array);
|
|
49
|
+
}
|
|
50
|
+
else if (array instanceof InlineArrayNode) {
|
|
51
|
+
guardKeysWithoutValue(array);
|
|
52
|
+
}
|
|
43
53
|
}
|
|
44
54
|
/**
|
|
45
55
|
* A dash right after a key at the same indentation reads as a dash subblock of that key.
|
|
@@ -47,10 +57,6 @@ export function setItemNode(item, node) {
|
|
|
47
57
|
* subblock gets its key written out, which ends the subblock.
|
|
48
58
|
*/
|
|
49
59
|
function guardDashSubblocks(block) {
|
|
50
|
-
if (!(block instanceof BlockArrayNode)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
guardEnd(block);
|
|
54
60
|
block.items.forEach((item, i) => {
|
|
55
61
|
const next = block.items[i + 1];
|
|
56
62
|
if (!item.key || !next?.bullet) {
|
|
@@ -64,6 +70,15 @@ function guardDashSubblocks(block) {
|
|
|
64
70
|
}
|
|
65
71
|
});
|
|
66
72
|
}
|
|
73
|
+
/** Keeps an item without a key from reading as the value of a key without a value on the line above. */
|
|
74
|
+
function guardKeysWithoutValue(array) {
|
|
75
|
+
array.items.forEach((item, i) => {
|
|
76
|
+
const next = array.items[i + 1];
|
|
77
|
+
if (item.key && item.value === null && !item.separator && next && !next.key) {
|
|
78
|
+
replaceValue(item, literal('null'));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
/** Spaces on the last line of the input would be read as the indentation of a missing value at the end. */
|
|
68
83
|
function guardEnd(at) {
|
|
69
84
|
let root = at;
|
|
@@ -292,7 +307,7 @@ export function addItem(array, key, value, position) {
|
|
|
292
307
|
? addToBlock(array, key, value, at)
|
|
293
308
|
: addToInline(array, key, value, at);
|
|
294
309
|
keepKeys(array, before, key === null);
|
|
295
|
-
|
|
310
|
+
guardGrammar(array);
|
|
296
311
|
return item;
|
|
297
312
|
}
|
|
298
313
|
/** The keys of the items before an edit, and whether they made a list. */
|
|
@@ -464,7 +479,7 @@ export function insertItem(array, item, at) {
|
|
|
464
479
|
}
|
|
465
480
|
}
|
|
466
481
|
keepKeys(array, before, key === null);
|
|
467
|
-
|
|
482
|
+
guardGrammar(array);
|
|
468
483
|
}
|
|
469
484
|
/**
|
|
470
485
|
* The comments an item owns at its edges, which an inline array cannot keep in place: the comment lines
|
|
@@ -643,7 +658,7 @@ export function removeItem(item, comments, nullable, mergeBlankLines = false) {
|
|
|
643
658
|
}
|
|
644
659
|
array[setItems](remaining);
|
|
645
660
|
keepKeys(array, before, true); // an item removed from a list renumbers the rest, whatever its key
|
|
646
|
-
|
|
661
|
+
guardGrammar(array);
|
|
647
662
|
}
|
|
648
663
|
function removeFromBlock(item, comments, mergeBlankLines) {
|
|
649
664
|
const { preamble, owned } = splitLeading(item.getFirstToken().leadingTrivia);
|
|
@@ -704,7 +719,7 @@ function emptyBlock(block, last, leaveNull) {
|
|
|
704
719
|
if (!leaveNull) {
|
|
705
720
|
replaceValue(owner, emptyArray(last.key ? '{}' : '[]'));
|
|
706
721
|
}
|
|
707
|
-
|
|
722
|
+
guardGrammar(owner.parent);
|
|
708
723
|
}
|
|
709
724
|
/** The document lost its last item: the empty array, or nothing, keeps what stood above the item. */
|
|
710
725
|
function emptyDocument(document, last, comments, leaveNull) {
|
|
@@ -806,7 +821,7 @@ export function setItemKey(item, key) {
|
|
|
806
821
|
if (array && before) {
|
|
807
822
|
before.keys.set(item, value);
|
|
808
823
|
keepKeys(array, before, false);
|
|
809
|
-
|
|
824
|
+
guardGrammar(array);
|
|
810
825
|
}
|
|
811
826
|
}
|
|
812
827
|
// ---- nodes in slots
|
|
@@ -993,8 +1008,9 @@ export function setByPath(document, path, value, create) {
|
|
|
993
1008
|
setItemValue(existing, value);
|
|
994
1009
|
return;
|
|
995
1010
|
}
|
|
996
|
-
else if (!create || typeof last === 'object') {
|
|
997
|
-
|
|
1011
|
+
else if (!create || typeof last === 'object') { // an item by its order is never made
|
|
1012
|
+
const missing = `No item '${segmentText(last)}' in the path ${pathText(path)}`;
|
|
1013
|
+
throw new NeonError(create ? missing : missing + ', and create is false');
|
|
998
1014
|
}
|
|
999
1015
|
const key = String(last);
|
|
1000
1016
|
if (array) {
|
package/dist/parser.js
CHANGED
|
@@ -228,26 +228,47 @@ class Parser {
|
|
|
228
228
|
const end = Pairs[open.kind];
|
|
229
229
|
const items = [];
|
|
230
230
|
const keys = new KeyCheck(this);
|
|
231
|
+
let next = null; // the key and the colon of the next item, read on the line after a key without a value
|
|
231
232
|
while (true) {
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
let head;
|
|
234
|
+
let colon;
|
|
235
|
+
if (next) {
|
|
236
|
+
[head, colon] = next;
|
|
237
|
+
next = null;
|
|
234
238
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
else {
|
|
240
|
+
while (this.consume('Newline')) {
|
|
241
|
+
;
|
|
242
|
+
}
|
|
243
|
+
const close = this.consumeToken(end);
|
|
244
|
+
if (close) {
|
|
245
|
+
return new InlineArrayNode(open, items, close);
|
|
246
|
+
}
|
|
247
|
+
head = this.parseValue();
|
|
248
|
+
colon = this.consumeColon();
|
|
238
249
|
}
|
|
239
|
-
const head = this.parseValue();
|
|
240
250
|
let item;
|
|
241
|
-
const colon = this.consumeToken('Colon', 'Equals');
|
|
242
251
|
if (colon) {
|
|
243
252
|
const key = keys.add(head);
|
|
244
|
-
|
|
253
|
+
let value = null;
|
|
254
|
+
const lineEnd = this.consume('Newline');
|
|
255
|
+
if (!this.isNext('Comma', end)) {
|
|
256
|
+
value = this.parseValue();
|
|
257
|
+
const nextColon = lineEnd ? this.consumeColon() : null;
|
|
258
|
+
if (nextColon) {
|
|
259
|
+
next = [value, nextColon];
|
|
260
|
+
value = null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
245
263
|
item = keys.item(null, key, colon, value);
|
|
246
264
|
}
|
|
247
265
|
else {
|
|
248
266
|
item = keys.item(null, null, null, head);
|
|
249
267
|
}
|
|
250
268
|
items.push(item);
|
|
269
|
+
if (next) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
251
272
|
const before = this.mark();
|
|
252
273
|
while (this.consume('Newline')) {
|
|
253
274
|
;
|
|
@@ -264,6 +285,19 @@ class Parser {
|
|
|
264
285
|
}
|
|
265
286
|
}
|
|
266
287
|
}
|
|
288
|
+
/** The colon or the equals sign after a key in an inline array, which may stand on a later line. */
|
|
289
|
+
consumeColon() {
|
|
290
|
+
const mark = this.mark();
|
|
291
|
+
while (this.consume('Newline')) {
|
|
292
|
+
;
|
|
293
|
+
}
|
|
294
|
+
const colon = this.consumeToken('Colon', 'Equals');
|
|
295
|
+
if (!colon) {
|
|
296
|
+
this.#i = mark.i;
|
|
297
|
+
this.#newlineTaken = mark.newlineTaken;
|
|
298
|
+
}
|
|
299
|
+
return colon;
|
|
300
|
+
}
|
|
267
301
|
/** One level deeper into arrays. */
|
|
268
302
|
enter(token) {
|
|
269
303
|
if (++this.#depth > MaxDepth) {
|
package/dist/values.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare class NeonDate {
|
|
|
37
37
|
static parse(text: string): NeonDate | null;
|
|
38
38
|
/** Converts the date to an instant; a local date needs the time zone offset in minutes or 'UTC'. */
|
|
39
39
|
toDate(zone?: 'UTC' | number): Date;
|
|
40
|
-
/** The date as the encoder writes it. */
|
|
40
|
+
/** The date as the encoder writes it, without the fraction of a second. */
|
|
41
41
|
toString(): string;
|
|
42
42
|
/** Equality of components, including the presence of an offset and the written fraction. */
|
|
43
43
|
equals(other: NeonDate): boolean;
|
package/dist/values.js
CHANGED
|
@@ -93,15 +93,12 @@ export class NeonDate {
|
|
|
93
93
|
res.setUTCHours(this.hour ?? 0, (this.minute ?? 0) - offset, this.second ?? 0, ms);
|
|
94
94
|
return res;
|
|
95
95
|
}
|
|
96
|
-
/** The date as the encoder writes it. */
|
|
96
|
+
/** The date as the encoder writes it, without the fraction of a second. */
|
|
97
97
|
toString() {
|
|
98
98
|
const pad = (n, width = 2) => String(n).padStart(width, '0');
|
|
99
99
|
let res = `${pad(this.year, 4)}-${pad(this.month)}-${pad(this.day)}`;
|
|
100
100
|
if (this.hour !== null) {
|
|
101
101
|
res += ` ${pad(this.hour)}:${pad(this.minute)}:${pad(this.second)}`;
|
|
102
|
-
if (this.fraction) {
|
|
103
|
-
res += '.' + this.fraction;
|
|
104
|
-
}
|
|
105
102
|
}
|
|
106
103
|
if (this.offset) {
|
|
107
104
|
res += ' ' + this.offset.replace(':', '');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nette/neon",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "NEON parser, encoder and lossless editor: change a value in a config file and keep its comments and formatting.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"bin": {
|
|
31
|
-
"neon-lint": "
|
|
31
|
+
"neon-lint": "bin/neon-lint.js"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
package/readme.md
CHANGED
|
@@ -71,8 +71,9 @@ note: '''
|
|
|
71
71
|
'''
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
JSON is a subset of NEON
|
|
75
|
-
|
|
74
|
+
JSON is a subset of NEON, except for a colon at the start of a line glued to its value
|
|
75
|
+
(`"a"` + new line + `:1`), which NEON reads as a literal. The format is described at
|
|
76
|
+
[ne-on.org](https://ne-on.org) and in the [manual](https://doc.nette.org/en/neon/format).
|
|
76
77
|
|
|
77
78
|
▶ Runnable examples: [examples/edge-cases](examples/edge-cases/)
|
|
78
79
|
|