links-notation 0.17.0 → 0.19.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 +68 -0
- package/README.ru.md +68 -0
- package/dist/index.js +313 -22
- package/index.d.ts +71 -1
- package/package.json +1 -1
- package/src/Link.js +4 -0
- package/src/ParseError.js +104 -0
- package/src/Parser.js +14 -3
- package/src/comments.js +164 -0
- package/src/grammar.pegjs +7 -1
- package/src/index.js +2 -0
- package/src/parser-generated.js +152 -18
- package/tests/Comments.test.js +160 -0
- package/tests/ParseErrorPosition.test.js +111 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// A parse error has to say where the document stopped making sense.
|
|
2
|
+
//
|
|
3
|
+
// The positions asserted here are the ones the Rust and C# ports report for the
|
|
4
|
+
// same input, so the implementations can be held to the same contract
|
|
5
|
+
// (https://github.com/link-foundation/links-notation/issues/302).
|
|
6
|
+
|
|
7
|
+
import { test, expect } from 'bun:test';
|
|
8
|
+
import { Parser, ParseError } from '../src/index.js';
|
|
9
|
+
|
|
10
|
+
const parser = new Parser();
|
|
11
|
+
|
|
12
|
+
function syntaxError(document) {
|
|
13
|
+
try {
|
|
14
|
+
const links = parser.parse(document);
|
|
15
|
+
throw new Error(
|
|
16
|
+
`expected ${JSON.stringify(document)} not to parse, got ${links.length} links`
|
|
17
|
+
);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
expect(error).toBeInstanceOf(ParseError);
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test('reports the line and column of the defect', () => {
|
|
25
|
+
// The defect is the second colon on line 2, and the two lines after it are
|
|
26
|
+
// fine.
|
|
27
|
+
const error = syntaxError(
|
|
28
|
+
'ci_gate x\nstage: rust: nextest\nnext stage\n clippy'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(error.line).toBe(2);
|
|
32
|
+
expect(error.column).toBe(12);
|
|
33
|
+
expect(error.found).toBe(':');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('offset agrees with the other implementations', () => {
|
|
37
|
+
// Rust and C# report offset 21, line 2, column 12 for this document.
|
|
38
|
+
const error = syntaxError('ci_gate x\nstage: rust: nextest\n');
|
|
39
|
+
|
|
40
|
+
expect(error.offset).toBe(21);
|
|
41
|
+
expect(error.line).toBe(2);
|
|
42
|
+
expect(error.column).toBe(12);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('reports the line a late defect is on', () => {
|
|
46
|
+
const error = syntaxError('a\nb\nc\nd\ne: f: g\nh\n');
|
|
47
|
+
|
|
48
|
+
expect(error.line).toBe(5);
|
|
49
|
+
expect(error.column).toBe(5);
|
|
50
|
+
expect(error.lineText).toBe('e: f: g');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('reports the end of the document when a group is never closed', () => {
|
|
54
|
+
const error = syntaxError('a (b\n');
|
|
55
|
+
|
|
56
|
+
expect(error.offset).toBe(5);
|
|
57
|
+
expect(error.line).toBe(2);
|
|
58
|
+
expect(error.column).toBe(1);
|
|
59
|
+
expect(error.found).toBeNull();
|
|
60
|
+
expect(error.message).toContain('end of input');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('reports an unmatched closing parenthesis', () => {
|
|
64
|
+
const error = syntaxError('a b)\n');
|
|
65
|
+
|
|
66
|
+
expect(error.offset).toBe(3);
|
|
67
|
+
expect(error.line).toBe(1);
|
|
68
|
+
expect(error.column).toBe(4);
|
|
69
|
+
expect(error.found).toBe(')');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('message says where the document broke', () => {
|
|
73
|
+
const error = syntaxError('ci_gate x\nstage: rust: nextest\n');
|
|
74
|
+
|
|
75
|
+
expect(error.message.startsWith('Syntax error at line 2, column 12:')).toBe(
|
|
76
|
+
true
|
|
77
|
+
);
|
|
78
|
+
expect(error.snippet).toBe('2 | stage: rust: nextest\n | ^');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('message quotes one line rather than the rest of the document', () => {
|
|
82
|
+
const document = `ci_gate x\nstage: rust: nextest\n${'trailing line\n'.repeat(500)}`;
|
|
83
|
+
|
|
84
|
+
const error = syntaxError(document);
|
|
85
|
+
|
|
86
|
+
expect(error.message).toContain('line 2, column 12');
|
|
87
|
+
expect(error.message).not.toContain('trailing line');
|
|
88
|
+
expect(error.message.length).toBeLessThan(200);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('message of a long line stays a message', () => {
|
|
92
|
+
const document = `${'a'.repeat(400)}: ${'b'.repeat(400)}: c`;
|
|
93
|
+
|
|
94
|
+
const error = syntaxError(document);
|
|
95
|
+
|
|
96
|
+
expect(error.line).toBe(1);
|
|
97
|
+
expect(error.column).toBe(803);
|
|
98
|
+
expect(error.message).toContain('...');
|
|
99
|
+
expect(error.message.length).toBeLessThan(300);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('the location the parser used to report is still there', () => {
|
|
103
|
+
const error = syntaxError('a: b: c');
|
|
104
|
+
|
|
105
|
+
expect(error.location.start).toEqual({ offset: 4, line: 1, column: 5 });
|
|
106
|
+
expect(error.cause).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('a document that parses reports nothing', () => {
|
|
110
|
+
expect(parser.parse('a: b\n c: d\n').length).toBe(2);
|
|
111
|
+
});
|