links-notation 0.12.0 → 0.15.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/.eslintrc.json +28 -0
- package/.prettierignore +4 -0
- package/.prettierrc +4 -0
- package/README.md +107 -7
- package/README.ru.md +3 -6
- package/bun.lock +189 -10
- package/dist/index.js +628 -263
- package/eslint.config.js +34 -0
- package/examples/empty_reference.js +43 -0
- package/examples/typescript-example.ts +95 -0
- package/index.d.ts +312 -0
- package/package.json +22 -7
- package/src/FormatOptions.js +1 -1
- package/src/Link.js +78 -34
- package/src/LinksGroup.js +2 -2
- package/src/Parser.js +51 -18
- package/src/grammar.pegjs +190 -15
- package/src/index.js +1 -1
- package/src/parser-generated.js +618 -231
- package/tests/ApiTests.test.js +2 -2
- package/tests/EdgeCaseParser.test.js +3 -3
- package/tests/EmptyReference.test.js +91 -0
- package/tests/FormatConfig.test.js +32 -10
- package/tests/IndentedIdSyntax.test.js +39 -39
- package/tests/Link.test.js +4 -4
- package/tests/LinksGroup.test.js +8 -5
- package/tests/MixedIndentationModes.test.js +35 -35
- package/tests/MultiQuoteParser.test.js +463 -0
- package/tests/MultilineParser.test.js +12 -12
- package/tests/MultilineQuotedString.test.js +13 -11
- package/tests/NestedIndentation.test.js +114 -0
- package/tests/NestedParser.test.js +5 -3
- package/tests/NestedSelfReference.test.js +4 -2
- package/tests/SingleLineParser.test.js +9 -9
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { test, expect } from 'bun:test';
|
|
2
|
+
import { Parser } from '../src/Parser.js';
|
|
3
|
+
import { formatLinks } from '../src/Link.js';
|
|
4
|
+
|
|
5
|
+
const parser = new Parser();
|
|
6
|
+
|
|
7
|
+
const format = (source) => formatLinks(parser.parse(source), false);
|
|
8
|
+
|
|
9
|
+
// https://github.com/link-foundation/links-notation/issues/282
|
|
10
|
+
// Indentation is structural at the root, so it must be structural inside
|
|
11
|
+
// parentheses too: a parenthesised group opens a nested context that starts
|
|
12
|
+
// fresh at indentation level zero and follows exactly the root's rules.
|
|
13
|
+
|
|
14
|
+
test('TestParenthesesReproduceRootIndentation', () => {
|
|
15
|
+
const root = format(`a
|
|
16
|
+
b
|
|
17
|
+
c
|
|
18
|
+
d`);
|
|
19
|
+
|
|
20
|
+
expect(root).toBe(`(a)
|
|
21
|
+
((a) (b))
|
|
22
|
+
(c)
|
|
23
|
+
((c) (d))`);
|
|
24
|
+
|
|
25
|
+
// The same lines inside parentheses keep the same structure, nested under the
|
|
26
|
+
// link the group belongs to.
|
|
27
|
+
const nested = format(`array (
|
|
28
|
+
a
|
|
29
|
+
b
|
|
30
|
+
c
|
|
31
|
+
d
|
|
32
|
+
)`);
|
|
33
|
+
|
|
34
|
+
expect(nested).toBe('(array ((a) ((a) (b)) (c) ((c) (d))))');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('TestParenthesesKeepRecordBoundaries', () => {
|
|
38
|
+
expect(
|
|
39
|
+
format(`value (
|
|
40
|
+
id "1"
|
|
41
|
+
label "one"
|
|
42
|
+
)`)
|
|
43
|
+
).toBe('(value ((id 1) (label one)))');
|
|
44
|
+
|
|
45
|
+
const links = parser.parse(`value (
|
|
46
|
+
id "1"
|
|
47
|
+
label "one"
|
|
48
|
+
)`);
|
|
49
|
+
expect(links.length).toBe(1);
|
|
50
|
+
|
|
51
|
+
const group = links[0].values[1];
|
|
52
|
+
expect(group.id).toBe(null);
|
|
53
|
+
expect(group.values.length).toBe(2);
|
|
54
|
+
expect(group.values[0].values[0].id).toBe('id');
|
|
55
|
+
expect(group.values[0].values[1].id).toBe('1');
|
|
56
|
+
expect(group.values[1].values[0].id).toBe('label');
|
|
57
|
+
expect(group.values[1].values[1].id).toBe('one');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('TestParenthesesKeepSeveralRecordsSeparate', () => {
|
|
61
|
+
expect(
|
|
62
|
+
format(`value (
|
|
63
|
+
(id "1" label "one")
|
|
64
|
+
(id "2" label "two")
|
|
65
|
+
)`)
|
|
66
|
+
).toBe('(value ((id 1 label one) (id 2 label two)))');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('TestParenthesesNestDeeply', () => {
|
|
70
|
+
expect(
|
|
71
|
+
format(`outer (
|
|
72
|
+
inner (
|
|
73
|
+
x 1
|
|
74
|
+
y 2
|
|
75
|
+
)
|
|
76
|
+
z 3
|
|
77
|
+
)`)
|
|
78
|
+
).toBe('(outer ((inner ((x 1) (y 2))) (z 3)))');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('TestSingleLineParenthesesAreUnchanged', () => {
|
|
82
|
+
expect(format('(a b c)')).toBe('(a b c)');
|
|
83
|
+
expect(format('(1: 2 3)')).toBe('(1: 2 3)');
|
|
84
|
+
expect(format('(a: b c)')).toBe('(a: b c)');
|
|
85
|
+
expect(format('((a b))')).toBe('((a b))');
|
|
86
|
+
expect(format('(a)')).toBe('(a)');
|
|
87
|
+
expect(format('()')).toBe('()');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('TestParenthesesWithIndentedIdSyntax', () => {
|
|
91
|
+
expect(
|
|
92
|
+
format(`(
|
|
93
|
+
a:
|
|
94
|
+
b
|
|
95
|
+
c
|
|
96
|
+
)`)
|
|
97
|
+
).toBe('(a: b c)');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('TestEmployeeRecordsKeepTheirFields', () => {
|
|
101
|
+
expect(
|
|
102
|
+
format(`empInfo
|
|
103
|
+
employees:
|
|
104
|
+
(
|
|
105
|
+
name (James Kirk)
|
|
106
|
+
age 40
|
|
107
|
+
)
|
|
108
|
+
(
|
|
109
|
+
name (Jean-Luc Picard)
|
|
110
|
+
age 45
|
|
111
|
+
)`)
|
|
112
|
+
).toBe(`(empInfo)
|
|
113
|
+
((empInfo) (employees: ((name (James Kirk)) (age 40)) ((name (Jean-Luc Picard)) (age 45))))`);
|
|
114
|
+
});
|
|
@@ -143,7 +143,7 @@ test('Nested links', () => {
|
|
|
143
143
|
// Validate regular formatting
|
|
144
144
|
const output = formatLinks(parsed);
|
|
145
145
|
expect(output).toBeTruthy();
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
// Validate that the structure is properly nested
|
|
148
148
|
expect(parsed.length).toBe(1);
|
|
149
149
|
});
|
|
@@ -153,7 +153,9 @@ test('Indentation parser', () => {
|
|
|
153
153
|
const result = parser.parse(input);
|
|
154
154
|
expect(result.length).toBeGreaterThan(0);
|
|
155
155
|
// Should have parent link
|
|
156
|
-
const hasParentLink = result.some(
|
|
156
|
+
const hasParentLink = result.some(
|
|
157
|
+
(l) => l.values && l.values.some((v) => v.id === 'parent')
|
|
158
|
+
);
|
|
157
159
|
expect(hasParentLink).toBe(true);
|
|
158
160
|
});
|
|
159
161
|
|
|
@@ -190,4 +192,4 @@ test('Multiple nested links roundtrip', () => {
|
|
|
190
192
|
// Validate round-trip
|
|
191
193
|
const output = formatLinks(parsed);
|
|
192
194
|
expect(output).toBe(input);
|
|
193
|
-
});
|
|
195
|
+
});
|
|
@@ -5,7 +5,8 @@ test('Nested self-referenced object in pair value', () => {
|
|
|
5
5
|
// Test case from PARSER_BUG.md
|
|
6
6
|
// This should parse a dict with two pairs, where the second pair's value
|
|
7
7
|
// is itself a self-referenced dict definition (obj_1: dict ...)
|
|
8
|
-
const notation =
|
|
8
|
+
const notation =
|
|
9
|
+
'(obj_0: dict ((str bmFtZQ==) (str ZGljdDE=)) ((str b3RoZXI=) (obj_1: dict ((str bmFtZQ==) (str ZGljdDI=)) ((str b3RoZXI=) obj_0))))';
|
|
9
10
|
|
|
10
11
|
const parser = new Parser();
|
|
11
12
|
const links = parser.parse(notation);
|
|
@@ -85,7 +86,8 @@ test('Nested self-referenced object in pair value', () => {
|
|
|
85
86
|
|
|
86
87
|
test('Self-reference as direct child works correctly', () => {
|
|
87
88
|
// This pattern should work (and did work before)
|
|
88
|
-
const notation =
|
|
89
|
+
const notation =
|
|
90
|
+
'(obj_0: list (int 1) (int 2) (obj_1: list (int 3) (int 4) obj_0))';
|
|
89
91
|
|
|
90
92
|
const parser = new Parser();
|
|
91
93
|
const links = parser.parse(notation);
|
|
@@ -26,16 +26,16 @@ test('Bug test 1', () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test('Quoted references', () => {
|
|
29
|
-
const source =
|
|
30
|
-
const target =
|
|
29
|
+
const source = '(a: \'b\' "c")';
|
|
30
|
+
const target = '(a: b c)';
|
|
31
31
|
const links = parser.parse(source);
|
|
32
32
|
const formattedLinks = formatLinks(links);
|
|
33
33
|
expect(formattedLinks).toBe(target);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
test('Quoted references with spaces', () => {
|
|
37
|
-
const source =
|
|
38
|
-
const target =
|
|
37
|
+
const source = "('a a': 'b b' \"c c\")";
|
|
38
|
+
const target = "('a a': 'b b' 'c c')";
|
|
39
39
|
const links = parser.parse(source);
|
|
40
40
|
const formattedLinks = formatLinks(links);
|
|
41
41
|
expect(formattedLinks).toBe(target);
|
|
@@ -70,7 +70,7 @@ test('Parse multiline link', () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
test('Parse quoted references', () => {
|
|
73
|
-
const input =
|
|
73
|
+
const input = '"has space" \'has:colon\'';
|
|
74
74
|
const result = parser.parse(input);
|
|
75
75
|
expect(result.length).toBe(1);
|
|
76
76
|
expect(result[0].id).toBe(null);
|
|
@@ -78,7 +78,7 @@ test('Parse quoted references', () => {
|
|
|
78
78
|
expect(result[0].values[0].id).toBe('has space');
|
|
79
79
|
expect(result[0].values[1].id).toBe('has:colon');
|
|
80
80
|
// Ensure formatting matches C# expectation
|
|
81
|
-
expect(formatLinks(result)).toBe(
|
|
81
|
+
expect(formatLinks(result)).toBe("('has space' 'has:colon')");
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
test('Parse values only', () => {
|
|
@@ -128,7 +128,7 @@ test('Test value link', () => {
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
test('Parse quoted references values only', () => {
|
|
131
|
-
const source =
|
|
131
|
+
const source = '"has space" \'has:colon\'';
|
|
132
132
|
const parser = new Parser();
|
|
133
133
|
const links = parser.parse(source);
|
|
134
134
|
expect(links).toBeTruthy();
|
|
@@ -161,7 +161,7 @@ test('Test special characters in quotes', () => {
|
|
|
161
161
|
let input = '("key:with:colons": "value(with)parens")';
|
|
162
162
|
let result = parser.parse(input);
|
|
163
163
|
expect(result.length).toBeGreaterThan(0);
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
input = "('key with spaces': 'value: with special chars')";
|
|
166
166
|
result = parser.parse(input);
|
|
167
167
|
expect(result.length).toBeGreaterThan(0);
|
|
@@ -306,4 +306,4 @@ test('Single line with id', () => {
|
|
|
306
306
|
const result = parser.parse(input);
|
|
307
307
|
expect(result.length).toBeGreaterThan(0);
|
|
308
308
|
expect(result[0].id).toBe('myid');
|
|
309
|
-
});
|
|
309
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2020",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"checkJs": false,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["examples/**/*.ts"]
|
|
15
|
+
}
|