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
package/tests/ApiTests.test.js
CHANGED
|
@@ -125,7 +125,7 @@ test('test_indented_id_syntax_roundtrip', () => {
|
|
|
125
125
|
|
|
126
126
|
// Validate that we can format with indented syntax using FormatOptions
|
|
127
127
|
const options = new FormatOptions();
|
|
128
|
-
options.maxInlineRefs = 1;
|
|
128
|
+
options.maxInlineRefs = 1; // Force indentation with more than 1 ref
|
|
129
129
|
options.preferInline = false;
|
|
130
130
|
const output = formatLinks(parsed, options);
|
|
131
131
|
expect(output).toBe(input);
|
|
@@ -164,7 +164,7 @@ id2:
|
|
|
164
164
|
|
|
165
165
|
// Validate that we can format with indented syntax using FormatOptions
|
|
166
166
|
const options = new FormatOptions();
|
|
167
|
-
options.maxInlineRefs = 1;
|
|
167
|
+
options.maxInlineRefs = 1; // Force indentation with more than 1 ref
|
|
168
168
|
options.preferInline = false;
|
|
169
169
|
const output = formatLinks(parsed, options);
|
|
170
170
|
expect(output).toBe(input);
|
|
@@ -93,11 +93,11 @@ test('Empty links', () => {
|
|
|
93
93
|
expect(result.length).toBe(1);
|
|
94
94
|
expect(result[0].id).toBe(null);
|
|
95
95
|
expect(result[0].values).toEqual([]);
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
// '(:)' is now forbidden
|
|
98
98
|
input = '(:)';
|
|
99
99
|
expect(() => parser.parse(input)).toThrow();
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
input = '(id:)';
|
|
102
102
|
result = parser.parse(input);
|
|
103
103
|
expect(result.length).toBe(1);
|
|
@@ -159,4 +159,4 @@ test('Invalid input', () => {
|
|
|
159
159
|
const input = '(invalid';
|
|
160
160
|
// Unclosed parentheses should throw an error
|
|
161
161
|
expect(() => parser.parse(input)).toThrow();
|
|
162
|
-
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Conformance tests for the empty reference (issue #288).
|
|
2
|
+
//
|
|
3
|
+
// A bare delimiter pair is the empty reference. The three delimiters `"`, `'`
|
|
4
|
+
// and ` (backtick) behave identically, and every longer n-quote run keeps the
|
|
5
|
+
// meaning it already had. The table below is shared with the Rust, Python, C#,
|
|
6
|
+
// Go, Java and PHP suites, so a document written by one implementation reads
|
|
7
|
+
// the same in all of them.
|
|
8
|
+
|
|
9
|
+
import { test, expect } from 'bun:test';
|
|
10
|
+
import { Parser } from '../src/Parser.js';
|
|
11
|
+
import { formatLinks } from '../src/Link.js';
|
|
12
|
+
|
|
13
|
+
const parser = new Parser();
|
|
14
|
+
|
|
15
|
+
// Render a parsed node unambiguously: every reference is wrapped in angle
|
|
16
|
+
// brackets so an empty one is visible as `<>`.
|
|
17
|
+
function render(node) {
|
|
18
|
+
if (!node.values || node.values.length === 0) {
|
|
19
|
+
return `<${node.id ?? ''}>`;
|
|
20
|
+
}
|
|
21
|
+
const head =
|
|
22
|
+
node.id === null || node.id === undefined ? '' : `<${node.id}>: `;
|
|
23
|
+
return `(${head}${node.values.map(render).join(' ')})`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parsesAs(input) {
|
|
27
|
+
return parser.parse(input).map(render).join('\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test('TestBareDelimiterPairIsTheEmptyReference', () => {
|
|
31
|
+
expect(parsesAs('(a "" b)')).toBe('(<a> <> <b>)');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('TestEveryDelimiterStyleYieldsTheSameEmptyReference', () => {
|
|
35
|
+
expect(parsesAs('(a "" b)')).toBe('(<a> <> <b>)');
|
|
36
|
+
expect(parsesAs("(a '' b)")).toBe('(<a> <> <b>)');
|
|
37
|
+
expect(parsesAs('(a `` b)')).toBe('(<a> <> <b>)');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('TestAdjacentEmptyReferencesStaySeparate', () => {
|
|
41
|
+
expect(parsesAs('(a "" "" b)')).toBe('(<a> <> <> <b>)');
|
|
42
|
+
expect(parsesAs("(a '' '' b)")).toBe('(<a> <> <> <b>)');
|
|
43
|
+
expect(parsesAs('(a `` `` b)')).toBe('(<a> <> <> <b>)');
|
|
44
|
+
expect(parsesAs('(a "" "" b)')).toBe('(<a> <> <> <b>)');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('TestNestedEmptyReferencesParse', () => {
|
|
48
|
+
expect(parsesAs('("" ("" 1))')).toBe('(<> (<> <1>))');
|
|
49
|
+
expect(parsesAs('("" (\'\' 1))')).toBe('(<> (<> <1>))');
|
|
50
|
+
expect(parsesAs('("x" ("" 1))')).toBe('(<x> (<> <1>))');
|
|
51
|
+
expect(parsesAs('("" ("x" 1))')).toBe('(<> (<x> <1>))');
|
|
52
|
+
expect(parsesAs('("" x ("" 1))')).toBe('(<> <x> (<> <1>))');
|
|
53
|
+
expect(parsesAs('("" 1 ("" 1))')).toBe('(<> <1> (<> <1>))');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('TestEmptyReferenceIsValidAsAnId', () => {
|
|
57
|
+
expect(parsesAs('("": 1)')).toBe('(<>: <1>)');
|
|
58
|
+
expect(parsesAs('(o: ("" (o: ("" 1))))')).toBe('(<o>: (<> (<o>: (<> <1>))))');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('TestNQuoteDelimitedBodiesAreUnchanged', () => {
|
|
62
|
+
// A run that encloses a substantive body keeps its n-quote meaning.
|
|
63
|
+
expect(parsesAs('(a ""x"" b)')).toBe('(<a> <x> <b>)');
|
|
64
|
+
expect(parsesAs('(x "" " "")')).toBe('(<x> < " >)');
|
|
65
|
+
expect(parsesAs("(x ' \" ')")).toBe('(<x> < " >)');
|
|
66
|
+
// An n-quote-delimited empty is still empty.
|
|
67
|
+
expect(parsesAs('(a """" b)')).toBe('(<a> <> <b>)');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('TestSingleSpaceStillReadsAsASpace', () => {
|
|
71
|
+
expect(parsesAs('(a " " b)')).toBe('(<a> < > <b>)');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('TestEmptyReferenceSurvivesARoundTrip', () => {
|
|
75
|
+
const inputs = [
|
|
76
|
+
'(a "" b)',
|
|
77
|
+
'(a "" "" b)',
|
|
78
|
+
'("" ("" 1))',
|
|
79
|
+
'("": 1)',
|
|
80
|
+
'(o: ("" (o: ("" 1))))',
|
|
81
|
+
];
|
|
82
|
+
for (const input of inputs) {
|
|
83
|
+
const links = parser.parse(input);
|
|
84
|
+
const formatted = formatLinks(links);
|
|
85
|
+
expect(parser.parse(formatted)).toEqual(links);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('TestEmptyReferenceIsWrittenAsADelimiterPair', () => {
|
|
90
|
+
expect(formatLinks(parser.parse('(a "" b)'))).toBe('(a "" b)');
|
|
91
|
+
});
|
|
@@ -13,8 +13,16 @@ test('Format config basic', () => {
|
|
|
13
13
|
test('Format with line length limit', () => {
|
|
14
14
|
// Create a link with many references that exceeds line length
|
|
15
15
|
const link = new Link('sequence', [
|
|
16
|
-
new Link('1'),
|
|
17
|
-
new Link('
|
|
16
|
+
new Link('1'),
|
|
17
|
+
new Link('2'),
|
|
18
|
+
new Link('3'),
|
|
19
|
+
new Link('4'),
|
|
20
|
+
new Link('5'),
|
|
21
|
+
new Link('6'),
|
|
22
|
+
new Link('7'),
|
|
23
|
+
new Link('8'),
|
|
24
|
+
new Link('9'),
|
|
25
|
+
new Link('10'),
|
|
18
26
|
]);
|
|
19
27
|
|
|
20
28
|
// Format with line length limit
|
|
@@ -22,7 +30,7 @@ test('Format with line length limit', () => {
|
|
|
22
30
|
const config = new FormatConfig({
|
|
23
31
|
indentLongLines: true,
|
|
24
32
|
maxLineLength: 30,
|
|
25
|
-
preferInline: false
|
|
33
|
+
preferInline: false,
|
|
26
34
|
});
|
|
27
35
|
|
|
28
36
|
const output = link.format(config);
|
|
@@ -32,12 +40,17 @@ test('Format with line length limit', () => {
|
|
|
32
40
|
|
|
33
41
|
test('Format with max inline refs', () => {
|
|
34
42
|
// Create a link with 4 references
|
|
35
|
-
const link = new Link('id', [
|
|
43
|
+
const link = new Link('id', [
|
|
44
|
+
new Link('1'),
|
|
45
|
+
new Link('2'),
|
|
46
|
+
new Link('3'),
|
|
47
|
+
new Link('4'),
|
|
48
|
+
]);
|
|
36
49
|
|
|
37
50
|
// Format with maxInlineRefs=3 (should trigger indentation)
|
|
38
51
|
const config = new FormatConfig({
|
|
39
52
|
maxInlineRefs: 3,
|
|
40
|
-
preferInline: false
|
|
53
|
+
preferInline: false,
|
|
41
54
|
});
|
|
42
55
|
|
|
43
56
|
const output = link.format(config);
|
|
@@ -49,7 +62,7 @@ test('Format with consecutive grouping', () => {
|
|
|
49
62
|
const links = [
|
|
50
63
|
new Link('SetA', [new Link('a')]),
|
|
51
64
|
new Link('SetA', [new Link('b')]),
|
|
52
|
-
new Link('SetA', [new Link('c')])
|
|
65
|
+
new Link('SetA', [new Link('c')]),
|
|
53
66
|
];
|
|
54
67
|
|
|
55
68
|
const config = new FormatConfig({ groupConsecutive: true });
|
|
@@ -75,12 +88,17 @@ test('Format config less parentheses', () => {
|
|
|
75
88
|
});
|
|
76
89
|
|
|
77
90
|
test('Format config custom indent', () => {
|
|
78
|
-
const link = new Link('id', [
|
|
91
|
+
const link = new Link('id', [
|
|
92
|
+
new Link('1'),
|
|
93
|
+
new Link('2'),
|
|
94
|
+
new Link('3'),
|
|
95
|
+
new Link('4'),
|
|
96
|
+
]);
|
|
79
97
|
|
|
80
98
|
const config = new FormatConfig({
|
|
81
99
|
maxInlineRefs: 3,
|
|
82
100
|
preferInline: false,
|
|
83
|
-
indentString: ' ' // 4 spaces
|
|
101
|
+
indentString: ' ', // 4 spaces
|
|
84
102
|
});
|
|
85
103
|
|
|
86
104
|
const output = link.format(config);
|
|
@@ -90,12 +108,16 @@ test('Format config custom indent', () => {
|
|
|
90
108
|
|
|
91
109
|
test('Roundtrip with line length formatting', () => {
|
|
92
110
|
// Create a simple link
|
|
93
|
-
const originalLink = new Link('test', [
|
|
111
|
+
const originalLink = new Link('test', [
|
|
112
|
+
new Link('a'),
|
|
113
|
+
new Link('b'),
|
|
114
|
+
new Link('c'),
|
|
115
|
+
]);
|
|
94
116
|
|
|
95
117
|
// Format with indentation
|
|
96
118
|
const config = new FormatConfig({
|
|
97
119
|
maxInlineRefs: 2,
|
|
98
|
-
preferInline: false
|
|
120
|
+
preferInline: false,
|
|
99
121
|
});
|
|
100
122
|
|
|
101
123
|
const formatted = originalLink.format(config);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { test, expect } from
|
|
2
|
-
import { Parser } from
|
|
3
|
-
import { formatLinks } from
|
|
1
|
+
import { test, expect } from 'bun:test';
|
|
2
|
+
import { Parser } from '../src/Parser.js';
|
|
3
|
+
import { formatLinks } from '../src/Link.js';
|
|
4
4
|
|
|
5
5
|
const parser = new Parser();
|
|
6
6
|
|
|
7
|
-
test(
|
|
7
|
+
test('Basic indented ID syntax', () => {
|
|
8
8
|
const indentedSyntax = `3:
|
|
9
9
|
papa
|
|
10
10
|
loves
|
|
11
11
|
mama`;
|
|
12
12
|
|
|
13
|
-
const inlineSyntax =
|
|
13
|
+
const inlineSyntax = '(3: papa loves mama)';
|
|
14
14
|
|
|
15
15
|
const indentedResult = parser.parse(indentedSyntax);
|
|
16
16
|
const inlineResult = parser.parse(inlineSyntax);
|
|
@@ -19,25 +19,25 @@ test("Basic indented ID syntax", () => {
|
|
|
19
19
|
expect(indentedResult).toEqual(inlineResult);
|
|
20
20
|
|
|
21
21
|
// Both should format to the same inline syntax
|
|
22
|
-
expect(formatLinks(indentedResult)).toBe(
|
|
23
|
-
expect(formatLinks(inlineResult)).toBe(
|
|
22
|
+
expect(formatLinks(indentedResult)).toBe('(3: papa loves mama)');
|
|
23
|
+
expect(formatLinks(inlineResult)).toBe('(3: papa loves mama)');
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
test(
|
|
26
|
+
test('Indented ID syntax with single value', () => {
|
|
27
27
|
const input = `greeting:
|
|
28
28
|
hello`;
|
|
29
29
|
|
|
30
30
|
const result = parser.parse(input);
|
|
31
31
|
const formatted = formatLinks(result);
|
|
32
32
|
|
|
33
|
-
expect(formatted).toBe(
|
|
33
|
+
expect(formatted).toBe('(greeting: hello)');
|
|
34
34
|
expect(result.length).toBe(1);
|
|
35
|
-
expect(result[0].id).toBe(
|
|
35
|
+
expect(result[0].id).toBe('greeting');
|
|
36
36
|
expect(result[0].values.length).toBe(1);
|
|
37
|
-
expect(result[0].values[0].id).toBe(
|
|
37
|
+
expect(result[0].values[0].id).toBe('hello');
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test(
|
|
40
|
+
test('Indented ID syntax with multiple values', () => {
|
|
41
41
|
const input = `action:
|
|
42
42
|
run
|
|
43
43
|
fast
|
|
@@ -46,13 +46,13 @@ test("Indented ID syntax with multiple values", () => {
|
|
|
46
46
|
const result = parser.parse(input);
|
|
47
47
|
const formatted = formatLinks(result);
|
|
48
48
|
|
|
49
|
-
expect(formatted).toBe(
|
|
49
|
+
expect(formatted).toBe('(action: run fast now)');
|
|
50
50
|
expect(result.length).toBe(1);
|
|
51
|
-
expect(result[0].id).toBe(
|
|
51
|
+
expect(result[0].id).toBe('action');
|
|
52
52
|
expect(result[0].values.length).toBe(3);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
test(
|
|
55
|
+
test('Indented ID syntax with numeric ID', () => {
|
|
56
56
|
const input = `42:
|
|
57
57
|
answer
|
|
58
58
|
to
|
|
@@ -61,10 +61,10 @@ test("Indented ID syntax with numeric ID", () => {
|
|
|
61
61
|
const result = parser.parse(input);
|
|
62
62
|
const formatted = formatLinks(result);
|
|
63
63
|
|
|
64
|
-
expect(formatted).toBe(
|
|
64
|
+
expect(formatted).toBe('(42: answer to everything)');
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
test(
|
|
67
|
+
test('Indented ID syntax with quoted ID', () => {
|
|
68
68
|
const input = `"complex id":
|
|
69
69
|
value1
|
|
70
70
|
value2`;
|
|
@@ -75,7 +75,7 @@ test("Indented ID syntax with quoted ID", () => {
|
|
|
75
75
|
expect(formatted).toBe("('complex id': value1 value2)");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
test(
|
|
78
|
+
test('Multiple indented ID links', () => {
|
|
79
79
|
const input = `first:
|
|
80
80
|
a
|
|
81
81
|
b
|
|
@@ -87,10 +87,10 @@ second:
|
|
|
87
87
|
const formatted = formatLinks(result);
|
|
88
88
|
|
|
89
89
|
expect(result.length).toBe(2);
|
|
90
|
-
expect(formatted).toBe(
|
|
90
|
+
expect(formatted).toBe('(first: a b)\n(second: c d)');
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
test(
|
|
93
|
+
test('Mixed indented and regular syntax', () => {
|
|
94
94
|
const input = `first:
|
|
95
95
|
a
|
|
96
96
|
b
|
|
@@ -101,12 +101,12 @@ third value`;
|
|
|
101
101
|
expect(result.length).toBe(3);
|
|
102
102
|
|
|
103
103
|
const formatted = formatLinks(result);
|
|
104
|
-
expect(formatted).toContain(
|
|
105
|
-
expect(formatted).toContain(
|
|
106
|
-
expect(formatted).toContain(
|
|
104
|
+
expect(formatted).toContain('(first: a b)');
|
|
105
|
+
expect(formatted).toContain('(second: c d)');
|
|
106
|
+
expect(formatted).toContain('third value');
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
test(
|
|
109
|
+
test('Unsupported colon-only syntax should fail', () => {
|
|
110
110
|
const input = `:
|
|
111
111
|
papa
|
|
112
112
|
loves
|
|
@@ -117,7 +117,7 @@ test("Unsupported colon-only syntax should fail", () => {
|
|
|
117
117
|
}).toThrow();
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
test(
|
|
120
|
+
test('Indented ID with deeper nesting', () => {
|
|
121
121
|
const input = `root:
|
|
122
122
|
child1
|
|
123
123
|
child2
|
|
@@ -129,36 +129,36 @@ test("Indented ID with deeper nesting", () => {
|
|
|
129
129
|
|
|
130
130
|
// The root should have child1 and child2 as values
|
|
131
131
|
const rootLink = result[0];
|
|
132
|
-
expect(rootLink.id).toBe(
|
|
132
|
+
expect(rootLink.id).toBe('root');
|
|
133
133
|
expect(rootLink.values.length).toBe(2);
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
test(
|
|
137
|
-
const input =
|
|
136
|
+
test('Empty indented ID should work', () => {
|
|
137
|
+
const input = 'empty:';
|
|
138
138
|
|
|
139
139
|
const result = parser.parse(input);
|
|
140
140
|
expect(result.length).toBe(1);
|
|
141
|
-
expect(result[0].id).toBe(
|
|
141
|
+
expect(result[0].id).toBe('empty');
|
|
142
142
|
expect(result[0].values.length).toBe(0);
|
|
143
143
|
|
|
144
144
|
const formatted = formatLinks(result);
|
|
145
|
-
expect(formatted).toBe(
|
|
145
|
+
expect(formatted).toBe('(empty)');
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
test(
|
|
148
|
+
test('Equivalence test comprehensive', () => {
|
|
149
149
|
const testCases = [
|
|
150
150
|
{
|
|
151
|
-
indented:
|
|
152
|
-
inline:
|
|
151
|
+
indented: 'test:\n one',
|
|
152
|
+
inline: '(test: one)',
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
|
-
indented:
|
|
156
|
-
inline:
|
|
155
|
+
indented: 'x:\n a\n b\n c',
|
|
156
|
+
inline: '(x: a b c)',
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
|
-
indented: "
|
|
160
|
-
inline:
|
|
161
|
-
}
|
|
159
|
+
indented: '"quoted":\n value',
|
|
160
|
+
inline: '("quoted": value)',
|
|
161
|
+
},
|
|
162
162
|
];
|
|
163
163
|
|
|
164
164
|
for (const testCase of testCases) {
|
|
@@ -168,4 +168,4 @@ test("Equivalence test comprehensive", () => {
|
|
|
168
168
|
expect(indentedResult).toEqual(inlineResult);
|
|
169
169
|
expect(formatLinks(indentedResult)).toBe(formatLinks(inlineResult));
|
|
170
170
|
}
|
|
171
|
-
});
|
|
171
|
+
});
|
package/tests/Link.test.js
CHANGED
|
@@ -38,8 +38,8 @@ test('Link escapeReference with special characters', () => {
|
|
|
38
38
|
expect(Link.escapeReference('has:colon')).toBe("'has:colon'");
|
|
39
39
|
expect(Link.escapeReference('has space')).toBe("'has space'");
|
|
40
40
|
expect(Link.escapeReference('has(paren)')).toBe("'has(paren)'");
|
|
41
|
-
expect(Link.escapeReference('has"quote')).toBe(
|
|
42
|
-
expect(Link.escapeReference("has'quote")).toBe(
|
|
41
|
+
expect(Link.escapeReference('has"quote')).toBe("'has\"quote'");
|
|
42
|
+
expect(Link.escapeReference("has'quote")).toBe('"has\'quote"');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
test('Link simplify', () => {
|
|
@@ -63,7 +63,7 @@ test('Link equals', () => {
|
|
|
63
63
|
const link1 = new Link('test', [new Link('child')]);
|
|
64
64
|
const link2 = new Link('test', [new Link('child')]);
|
|
65
65
|
const link3 = new Link('different', [new Link('child')]);
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
expect(link1.equals(link2)).toBe(true);
|
|
68
68
|
expect(link1.equals(link3)).toBe(false);
|
|
69
|
-
});
|
|
69
|
+
});
|
package/tests/LinksGroup.test.js
CHANGED
|
@@ -6,7 +6,7 @@ test('LinksGroup constructor', () => {
|
|
|
6
6
|
const element = new Link('root');
|
|
7
7
|
const children = [new Link('child1'), new Link('child2')];
|
|
8
8
|
const group = new LinksGroup(element, children);
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
expect(group.element).toBe(element);
|
|
11
11
|
expect(group.children).toBe(children);
|
|
12
12
|
});
|
|
@@ -16,10 +16,10 @@ test('LinksGroup toList flattens structure', () => {
|
|
|
16
16
|
const child1 = new Link('child1');
|
|
17
17
|
const child2 = new Link('child2');
|
|
18
18
|
const grandchild = new Link('grandchild');
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
const childGroup = new LinksGroup(child2, [grandchild]);
|
|
21
21
|
const group = new LinksGroup(root, [child1, childGroup]);
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
const list = group.toList();
|
|
24
24
|
expect(list.length).toBe(4);
|
|
25
25
|
expect(list[0]).toBe(root);
|
|
@@ -45,7 +45,10 @@ test('LinksGroup constructor equivalent test', () => {
|
|
|
45
45
|
const children = [new Link('child1'), new Link('child2')];
|
|
46
46
|
|
|
47
47
|
// Create a group with an id
|
|
48
|
-
const group = new LinksGroup(new Link('group'), [
|
|
48
|
+
const group = new LinksGroup(new Link('group'), [
|
|
49
|
+
new LinksGroup(root),
|
|
50
|
+
...children.map((c) => new LinksGroup(c)),
|
|
51
|
+
]);
|
|
49
52
|
|
|
50
53
|
expect(group.element.id).toBe('group');
|
|
51
54
|
expect(group.children.length).toBe(3);
|
|
@@ -64,4 +67,4 @@ test('LinksGroup append to links list test', () => {
|
|
|
64
67
|
expect(list[0]).toBe(element);
|
|
65
68
|
expect(list[1]).toBe(children[0]);
|
|
66
69
|
expect(list[2]).toBe(children[1]);
|
|
67
|
-
});
|
|
70
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { test, expect } from
|
|
2
|
-
import { Parser } from
|
|
3
|
-
import { formatLinks } from
|
|
1
|
+
import { test, expect } from 'bun:test';
|
|
2
|
+
import { Parser } from '../src/Parser.js';
|
|
3
|
+
import { formatLinks } from '../src/Link.js';
|
|
4
4
|
|
|
5
5
|
const parser = new Parser();
|
|
6
6
|
|
|
7
|
-
test(
|
|
7
|
+
test('Hero example mixed modes', () => {
|
|
8
8
|
const input = `empInfo
|
|
9
9
|
employees:
|
|
10
10
|
(
|
|
@@ -24,14 +24,14 @@ test("Hero example mixed modes", () => {
|
|
|
24
24
|
|
|
25
25
|
expect(result.length).toBeGreaterThan(0);
|
|
26
26
|
const formatted = formatLinks(result);
|
|
27
|
-
expect(formatted).toContain(
|
|
28
|
-
expect(formatted).toContain(
|
|
29
|
-
expect(formatted).toContain(
|
|
30
|
-
expect(formatted).toContain(
|
|
31
|
-
expect(formatted).toContain(
|
|
27
|
+
expect(formatted).toContain('empInfo');
|
|
28
|
+
expect(formatted).toContain('employees:');
|
|
29
|
+
expect(formatted).toContain('James Kirk');
|
|
30
|
+
expect(formatted).toContain('Jean-Luc Picard');
|
|
31
|
+
expect(formatted).toContain('Wesley Crusher');
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
test(
|
|
34
|
+
test('Hero example alternative format', () => {
|
|
35
35
|
const input = `empInfo
|
|
36
36
|
(
|
|
37
37
|
employees:
|
|
@@ -53,14 +53,14 @@ test("Hero example alternative format", () => {
|
|
|
53
53
|
|
|
54
54
|
expect(result.length).toBeGreaterThan(0);
|
|
55
55
|
const formatted = formatLinks(result);
|
|
56
|
-
expect(formatted).toContain(
|
|
57
|
-
expect(formatted).toContain(
|
|
58
|
-
expect(formatted).toContain(
|
|
59
|
-
expect(formatted).toContain(
|
|
60
|
-
expect(formatted).toContain(
|
|
56
|
+
expect(formatted).toContain('empInfo');
|
|
57
|
+
expect(formatted).toContain('employees:');
|
|
58
|
+
expect(formatted).toContain('James Kirk');
|
|
59
|
+
expect(formatted).toContain('Jean-Luc Picard');
|
|
60
|
+
expect(formatted).toContain('Wesley Crusher');
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
test(
|
|
63
|
+
test('Hero example equivalence', () => {
|
|
64
64
|
const version1 = `empInfo
|
|
65
65
|
employees:
|
|
66
66
|
(
|
|
@@ -102,7 +102,7 @@ test("Hero example equivalence", () => {
|
|
|
102
102
|
expect(formatted1).toBe(formatted2);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
test(
|
|
105
|
+
test('Set context without colon', () => {
|
|
106
106
|
const input = `empInfo
|
|
107
107
|
employees`;
|
|
108
108
|
|
|
@@ -110,11 +110,11 @@ test("Set context without colon", () => {
|
|
|
110
110
|
|
|
111
111
|
expect(result.length).toBeGreaterThan(0);
|
|
112
112
|
const formatted = formatLinks(result);
|
|
113
|
-
expect(formatted).toContain(
|
|
114
|
-
expect(formatted).toContain(
|
|
113
|
+
expect(formatted).toContain('empInfo');
|
|
114
|
+
expect(formatted).toContain('employees');
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
test(
|
|
117
|
+
test('Sequence context with colon', () => {
|
|
118
118
|
const input = `employees:
|
|
119
119
|
James Kirk
|
|
120
120
|
Jean-Luc Picard
|
|
@@ -125,13 +125,13 @@ test("Sequence context with colon", () => {
|
|
|
125
125
|
expect(result.length).toBeGreaterThan(0);
|
|
126
126
|
expect(result.length).toBe(1);
|
|
127
127
|
const formatted = formatLinks(result);
|
|
128
|
-
expect(formatted).toContain(
|
|
129
|
-
expect(formatted).toContain(
|
|
130
|
-
expect(formatted).toContain(
|
|
131
|
-
expect(formatted).toContain(
|
|
128
|
+
expect(formatted).toContain('employees:');
|
|
129
|
+
expect(formatted).toContain('James Kirk');
|
|
130
|
+
expect(formatted).toContain('Jean-Luc Picard');
|
|
131
|
+
expect(formatted).toContain('Wesley Crusher');
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
test(
|
|
134
|
+
test('Sequence context with complex values', () => {
|
|
135
135
|
const input = `employees:
|
|
136
136
|
(
|
|
137
137
|
name (James Kirk)
|
|
@@ -147,12 +147,12 @@ test("Sequence context with complex values", () => {
|
|
|
147
147
|
expect(result.length).toBeGreaterThan(0);
|
|
148
148
|
expect(result.length).toBe(1);
|
|
149
149
|
const formatted = formatLinks(result);
|
|
150
|
-
expect(formatted).toContain(
|
|
151
|
-
expect(formatted).toContain(
|
|
152
|
-
expect(formatted).toContain(
|
|
150
|
+
expect(formatted).toContain('employees:');
|
|
151
|
+
expect(formatted).toContain('James Kirk');
|
|
152
|
+
expect(formatted).toContain('Jean-Luc Picard');
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
test(
|
|
155
|
+
test('Nested set and sequence contexts', () => {
|
|
156
156
|
const input = `company
|
|
157
157
|
departments:
|
|
158
158
|
engineering
|
|
@@ -165,12 +165,12 @@ test("Nested set and sequence contexts", () => {
|
|
|
165
165
|
|
|
166
166
|
expect(result.length).toBeGreaterThan(0);
|
|
167
167
|
const formatted = formatLinks(result);
|
|
168
|
-
expect(formatted).toContain(
|
|
169
|
-
expect(formatted).toContain(
|
|
170
|
-
expect(formatted).toContain(
|
|
168
|
+
expect(formatted).toContain('company');
|
|
169
|
+
expect(formatted).toContain('departments:');
|
|
170
|
+
expect(formatted).toContain('employees:');
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
-
test(
|
|
173
|
+
test('Deeply nested mixed modes', () => {
|
|
174
174
|
const input = `root
|
|
175
175
|
level1
|
|
176
176
|
level2:
|
|
@@ -183,6 +183,6 @@ test("Deeply nested mixed modes", () => {
|
|
|
183
183
|
|
|
184
184
|
expect(result.length).toBeGreaterThan(0);
|
|
185
185
|
const formatted = formatLinks(result);
|
|
186
|
-
expect(formatted).toContain(
|
|
187
|
-
expect(formatted).toContain(
|
|
186
|
+
expect(formatted).toContain('root');
|
|
187
|
+
expect(formatted).toContain('level2:');
|
|
188
188
|
});
|