• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DrRataplan / prettier-plugin-jinks / 23786320315

31 Mar 2026 07:45AM UTC coverage: 93.968% (+0.8%) from 93.154%
23786320315

push

github

DrRataplan
chore: fix package.json repo casing

147 of 172 branches covered (85.47%)

Branch coverage included in aggregate %.

1146 of 1204 relevant lines covered (95.18%)

94.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.2
/src/parser.ts
1
import type { TokenType } from "./Token.ts";
2✔
2
import { TokenTypes } from "./Token.ts";
2✔
3
import type { Token } from "./tokenize.ts";
2✔
4

2✔
5
export type XQExprNode = {
2✔
6
        type: TokenType.XQEXPR;
2✔
7
        value: string;
2✔
8
        start: number;
2✔
9
        end: number;
2✔
10
};
2✔
11

2✔
12
export type Node =
2✔
13
        | BlockNode
2✔
14
        | ElseNode
2✔
15
        | ElifNode
2✔
16
        | ForNode
2✔
17
        | IfNode
2✔
18
        | ImportNode
2✔
19
        | IncludeNode
2✔
20
        | LetNode
2✔
21
        | SimpleNode
2✔
22
        | TemplateNode
2✔
23
        | ValueNode
2✔
24
        | XQExprNode;
2✔
25

2✔
26
type SimpleNode = {
2✔
27
        type:
2✔
28
                | TokenType.COMMENT
2✔
29
                | TokenType.ENDBLOCK
2✔
30
                | TokenType.ENDTEMPLATE
2✔
31
                | TokenType.ENDFOR
2✔
32
                | TokenType.ENDIF
2✔
33
                | TokenType.ENDLET
2✔
34
                | TokenType.FRONTMATTER
2✔
35
                | TokenType.RAW
2✔
36
                | TokenType.TEXT;
2✔
37
        value: string;
2✔
38
        start: number;
2✔
39
        end: number;
2✔
40
};
2✔
41

2✔
42
type ElifNode = {
2✔
43
        type: TokenType.ELIF;
2✔
44
        expr: XQExprNode;
2✔
45
        body: Node[];
2✔
46
        start: number;
2✔
47
        end: number;
2✔
48
};
2✔
49

2✔
50
type ElseNode = {
2✔
51
        type: TokenType.ELSE;
2✔
52
        body: Node[];
2✔
53
        start: number;
2✔
54
        end: number;
2✔
55
};
2✔
56

2✔
57
type ImportNode = {
2✔
58
        type: TokenType.IMPORT;
2✔
59
        at: string | null;
2✔
60
        uri: string;
2✔
61
        as: string;
2✔
62
        start: number;
2✔
63
        end: number;
2✔
64
};
2✔
65

2✔
66
export type RootNode = {
2✔
67
        type: "root";
2✔
68
        body: Node[];
2✔
69
        start: number;
2✔
70
        end: number;
2✔
71
};
2✔
72

2✔
73
type ValueNode = {
2✔
74
        type: TokenType.VALUE;
2✔
75
        expr: XQExprNode;
2✔
76
        start: number;
2✔
77
        end: number;
2✔
78
};
2✔
79

2✔
80
type IncludeNode = {
2✔
81
        type: TokenType.INCLUDE;
2✔
82
        target: string;
2✔
83
        start: number;
2✔
84
        end: number;
2✔
85
};
2✔
86

2✔
87
type ForNode = {
2✔
88
        type: TokenType.FOR;
2✔
89
        var: string;
2✔
90
        expr: XQExprNode;
2✔
91
        body: Node[];
2✔
92
        start: number;
2✔
93
        end: number;
2✔
94
};
2✔
95

2✔
96
type LetNode = {
2✔
97
        type: TokenType.LET;
2✔
98
        var: string;
2✔
99
        expr: XQExprNode;
2✔
100
        body: Node[];
2✔
101
        start: number;
2✔
102
        end: number;
2✔
103
};
2✔
104

2✔
105
type IfNode = {
2✔
106
        type: TokenType.IF;
2✔
107
        expr: XQExprNode;
2✔
108
        consequent: Node[];
2✔
109
        alternates: Alternate[];
2✔
110
        start: number;
2✔
111
        end: number;
2✔
112
};
2✔
113

2✔
114
type Alternate = ElifNode | ElseNode;
2✔
115

2✔
116
type BlockNode = {
2✔
117
        type: TokenType.BLOCK;
2✔
118
        name: string;
2✔
119
        order: string | null;
2✔
120
        body: Node[];
2✔
121
        start: number;
2✔
122
        end: number;
2✔
123
};
2✔
124
type TemplateNode = {
2✔
125
        type: TokenType.TEMPLATE | TokenType.TEMPLATE_OVERRIDE;
2✔
126
        name: string;
2✔
127
        order: string | null;
2✔
128
        body: Node[];
2✔
129
        start: number;
2✔
130
        end: number;
2✔
131
};
2✔
132
export default function parse(tokens: Token[]): RootNode {
2✔
133
        let pos = 0;
39✔
134

39✔
135
        function peek(): Token {
39✔
136
                return tokens[pos]!;
1,074✔
137
        }
1,074✔
138
        function consume() {
39✔
139
                return tokens[pos++];
540✔
140
        }
540✔
141
        function eof() {
39✔
142
                return pos >= tokens.length;
689✔
143
        }
689✔
144

39✔
145
        function parseBody(endTypes: TokenType.TokenType[] | null) {
39✔
146
                const nodes: Node[] = [];
142✔
147
                while (!eof()) {
142✔
148
                        const t = peek();
547✔
149
                        if (endTypes && endTypes.includes(t.type)) break;
547✔
150
                        nodes.push(parseNode());
449✔
151
                }
449✔
152
                return nodes;
28✔
153
        }
142✔
154

39✔
155
        function parseNode(): Node {
39✔
156
                const t = peek();
449✔
157

449✔
158
                switch (t.type) {
449✔
159
                        case TokenTypes.FRONTMATTER: {
449✔
160
                                consume();
4✔
161
                                return {
4✔
162
                                        type: TokenTypes.FRONTMATTER,
4✔
163
                                        value: t.value,
4✔
164
                                        start: t.start,
4✔
165
                                        end: t.end,
4✔
166
                                };
4✔
167
                        }
4✔
168
                        case TokenTypes.COMMENT: {
449✔
169
                                consume();
4✔
170
                                return {
4✔
171
                                        type: TokenTypes.COMMENT,
4✔
172
                                        value: t.value,
4✔
173
                                        start: t.start,
4✔
174
                                        end: t.end,
4✔
175
                                };
4✔
176
                        }
4✔
177
                        case TokenTypes.TEXT: {
449✔
178
                                consume();
269✔
179
                                return {
269✔
180
                                        type: TokenTypes.TEXT,
269✔
181
                                        value: t.value,
269✔
182
                                        start: t.start,
269✔
183
                                        end: t.end,
269✔
184
                                };
269✔
185
                        }
269✔
186
                        case TokenTypes.RAW: {
449✔
187
                                consume();
1✔
188
                                return {
1✔
189
                                        type: TokenTypes.RAW,
1✔
190
                                        value: t.value,
1✔
191
                                        start: t.start,
1✔
192
                                        end: t.end,
1✔
193
                                };
1✔
194
                        }
1✔
195
                        case TokenTypes.VALUE: {
449✔
196
                                consume();
95✔
197
                                return {
95✔
198
                                        type: TokenTypes.VALUE,
95✔
199
                                        expr: t.expr,
95✔
200
                                        start: t.start,
95✔
201
                                        end: t.end,
95✔
202
                                };
95✔
203
                        }
95✔
204
                        case TokenTypes.IMPORT: {
449✔
205
                                consume();
1✔
206
                                return {
1✔
207
                                        type: TokenTypes.IMPORT,
1✔
208
                                        uri: t.uri,
1✔
209
                                        as: t.as,
1✔
210
                                        at: t.at,
1✔
211
                                        start: t.start,
1✔
212
                                        end: t.end,
1✔
213
                                };
1✔
214
                        }
1✔
215
                        case TokenTypes.INCLUDE: {
449!
216
                                consume();
×
217
                                return {
×
218
                                        type: TokenTypes.INCLUDE,
×
219
                                        target: t.target,
×
220
                                        start: t.start,
×
221
                                        end: t.end,
×
222
                                };
×
223
                        }
×
224
                        case TokenTypes.FOR: {
449✔
225
                                consume();
13✔
226
                                const body = parseBody([TokenTypes.ENDFOR]);
13✔
227
                                if (eof())
13✔
228
                                        throw new SyntaxError("[% for %] without [% endfor %]");
13✔
229
                                const endFor = consume()!;
2✔
230
                                return {
2✔
231
                                        type: TokenTypes.FOR,
2✔
232
                                        var: t.var,
2✔
233
                                        expr: t.expr,
2✔
234
                                        body,
2✔
235
                                        start: t.start,
2✔
236
                                        end: endFor.end,
2✔
237
                                };
2✔
238
                        }
2✔
239
                        case TokenTypes.LET: {
449✔
240
                                consume();
2✔
241
                                const body = parseBody([TokenTypes.ENDLET]);
2✔
242
                                if (eof())
2✔
243
                                        throw new SyntaxError("[% let %] without [% endlet %]");
2✔
244
                                const endLet = consume()!;
1✔
245
                                return {
1✔
246
                                        type: TokenTypes.LET,
1✔
247
                                        var: t.var,
1✔
248
                                        expr: t.expr,
1✔
249
                                        body,
1✔
250
                                        start: t.start,
1✔
251
                                        end: endLet.end,
1✔
252
                                };
1✔
253
                        }
1✔
254
                        case TokenTypes.IF: {
449✔
255
                                consume();
38✔
256
                                const consequent = parseBody([
38✔
257
                                        TokenTypes.ELIF,
38✔
258
                                        TokenTypes.ELSE,
38✔
259
                                        TokenTypes.ENDIF,
38✔
260
                                ]);
38✔
261
                                const alternates: Alternate[] = [];
38✔
262
                                while (!eof()) {
38✔
263
                                        const next = peek();
41✔
264
                                        if (next.type === TokenTypes.ELIF) {
41✔
265
                                                consume();
4✔
266
                                                const elifBody = parseBody([
4✔
267
                                                        TokenTypes.ELIF,
4✔
268
                                                        TokenTypes.ELSE,
4✔
269
                                                        TokenTypes.ENDIF,
4✔
270
                                                ]);
4✔
271
                                                alternates.push({
4✔
272
                                                        type: TokenTypes.ELIF,
4✔
273
                                                        expr: next.expr,
4✔
274
                                                        body: elifBody,
4✔
275
                                                        start: next.start,
4✔
276
                                                        end:
4✔
277
                                                                elifBody.length > 0
4✔
278
                                                                        ? elifBody[elifBody.length - 1]!.end
4✔
279
                                                                        : next.end,
4!
280
                                                });
4✔
281
                                        } else if (next.type === TokenTypes.ELSE) {
41✔
282
                                                consume();
31✔
283
                                                const elseBody = parseBody([TokenTypes.ENDIF]);
31✔
284
                                                alternates.push({
31✔
285
                                                        type: TokenTypes.ELSE,
31✔
286
                                                        body: elseBody,
31✔
287
                                                        start: next.start,
31✔
288
                                                        end:
31✔
289
                                                                elseBody.length > 0
31✔
290
                                                                        ? elseBody[elseBody.length - 1]!.end
31✔
291
                                                                        : next.end,
31!
292
                                                });
31✔
293
                                                break;
31✔
294
                                        } else if (next.type === TokenTypes.ENDIF) {
37✔
295
                                                break;
6✔
296
                                        } else {
6!
297
                                                break;
×
298
                                        }
×
299
                                }
41✔
300
                                if (eof() || peek().type !== TokenTypes.ENDIF)
38✔
301
                                        throw new SyntaxError("[% if %] without [% endif %]");
38✔
302
                                const endIf = consume()!;
3✔
303
                                return {
3✔
304
                                        type: TokenTypes.IF,
3✔
305
                                        expr: t.expr,
3✔
306
                                        consequent,
3✔
307
                                        alternates,
3✔
308
                                        start: t.start,
3✔
309
                                        end: endIf.end,
3✔
310
                                };
3✔
311
                        }
3✔
312
                        case TokenTypes.BLOCK: {
449✔
313
                                consume();
8✔
314
                                const body = parseBody([TokenTypes.ENDBLOCK]);
8✔
315
                                if (eof())
8✔
316
                                        throw new SyntaxError("[% block %] without [% endblock %]");
8✔
317
                                const endBlock = consume()!;
1✔
318
                                return {
1✔
319
                                        type: TokenTypes.BLOCK,
1✔
320
                                        name: t.name,
1✔
321
                                        order: t.order,
1✔
322
                                        body,
1✔
323
                                        start: t.start,
1✔
324
                                        end: endBlock.end,
1✔
325
                                };
1✔
326
                        }
1✔
327
                        case TokenTypes.TEMPLATE_OVERRIDE:
449✔
328
                        case TokenTypes.TEMPLATE: {
449✔
329
                                consume();
7✔
330
                                const body = parseBody([TokenTypes.ENDTEMPLATE]);
7✔
331
                                if (eof())
7✔
332
                                        throw new SyntaxError(
7✔
333
                                                "[% template %] without [% endtemplate %]",
1✔
334
                                        );
1✔
335
                                const endTemplate = consume()!;
2✔
336
                                return {
2✔
337
                                        type: t.type,
2✔
338
                                        name: t.name,
2✔
339
                                        order: t.order,
2✔
340
                                        body,
2✔
341
                                        start: t.start,
2✔
342
                                        end: endTemplate.end,
2✔
343
                                };
2✔
344
                        }
2✔
345
                        case TokenTypes.ENDFOR:
449✔
346
                                throw new SyntaxError("[% endfor %] without [% for %]");
1✔
347
                        case TokenTypes.ENDLET:
449✔
348
                                throw new SyntaxError("[% endlet %] without [% let %]");
1✔
349
                        case TokenTypes.ENDIF:
449✔
350
                                throw new SyntaxError("[% endif %] without [% if %]");
1✔
351
                        case TokenTypes.ENDBLOCK:
449✔
352
                                throw new SyntaxError("[% endblock %] without [% block %]");
1✔
353
                        case TokenTypes.ENDTEMPLATE:
449✔
354
                                throw new SyntaxError(
1✔
355
                                        "[% endtemplate %] without [% template %]",
1✔
356
                                );
1✔
357
                        case TokenTypes.ELIF:
449✔
358
                                throw new SyntaxError("[% elif %] without [% if %]");
1✔
359
                        case TokenTypes.ELSE:
449✔
360
                                throw new SyntaxError("[% else %] without [% if %]");
1✔
361
                }
449✔
362
        }
449✔
363

39✔
364
        const body = parseBody(null);
39✔
365
        const start = body.length > 0 ? body[0]!.start : 0;
39!
366
        const end = body.length > 0 ? body[body.length - 1]!.end : 0;
39!
367
        return { type: "root", body, start, end };
39✔
368
}
39✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc