@reteps/tree-sitter-htmlmustache 0.0.18

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/grammar.js ADDED
@@ -0,0 +1,426 @@
1
+ /**
2
+ * @file HTML grammar for tree-sitter
3
+ * @author Max Brunsfeld <maxbrunsfeld@gmail.com>
4
+ * @author Amaan Qureshi <amaanq12@gmail.com>
5
+ * @license MIT
6
+ */
7
+
8
+ /// <reference types="tree-sitter-cli/dsl" />
9
+ // @ts-check
10
+
11
+ module.exports = grammar({
12
+ name: 'htmlmustache',
13
+
14
+ extras: ($) => [$.html_comment, /\s+/],
15
+
16
+ externals: ($) => [
17
+ $._html_start_tag_name,
18
+ $._html_script_start_tag_name,
19
+ $._html_style_start_tag_name,
20
+ $._html_raw_start_tag_name,
21
+ $._html_end_tag_name,
22
+ $.html_erroneous_end_tag_name,
23
+ '/>',
24
+ $._html_implicit_end_tag,
25
+ $.html_raw_text,
26
+ $.html_comment,
27
+ // Mustache externals
28
+ $._mustache_start_tag_name,
29
+ $._mustache_end_tag_name,
30
+ $._mustache_erroneous_end_tag_name,
31
+ $._mustache_end_tag_html_implicit_end_tag,
32
+ ],
33
+
34
+ rules: {
35
+ document: ($) => repeat($._node),
36
+
37
+ html_doctype: ($) =>
38
+ seq('<!', alias($._html_doctype, 'doctype'), /[^>]+/, '>'),
39
+
40
+ _html_doctype: (_) => /[Dd][Oo][Cc][Tt][Yy][Pp][Ee]/,
41
+
42
+ _node: ($) => choice($._html_node, $._mustache_node),
43
+
44
+ _html_node: ($) =>
45
+ choice(
46
+ $.html_doctype,
47
+ $.html_entity,
48
+ $.html_element,
49
+ $.html_script_element,
50
+ $.html_style_element,
51
+ $.html_raw_element,
52
+ $.html_erroneous_end_tag,
53
+ $.text,
54
+ alias($._text_brace, $.text),
55
+ alias($._text_ampersand, $.text),
56
+ ),
57
+
58
+ _mustache_node: ($) =>
59
+ choice(
60
+ $.mustache_triple,
61
+ $.mustache_comment,
62
+ $.mustache_partial,
63
+ $.mustache_section,
64
+ $.mustache_inverted_section,
65
+ $.mustache_interpolation,
66
+ ),
67
+ // Mustache rules - order matters for parsing precedence
68
+ mustache_triple: ($) => seq('{{{', $._mustache_expression, '}}}'),
69
+
70
+ mustache_comment: ($) =>
71
+ seq('{{!', alias($._mustache_content, $.mustache_comment_content), '}}'),
72
+
73
+ _mustache_content: ($) => /[^}]+/,
74
+
75
+ mustache_partial: ($) =>
76
+ seq('{{>', alias($._mustache_content, $.mustache_partial_content), '}}'),
77
+
78
+ mustache_interpolation: ($) => seq('{{', $._mustache_expression, '}}'),
79
+
80
+ mustache_section: ($) =>
81
+ seq(
82
+ $.mustache_section_begin,
83
+ repeat($._node),
84
+ choice($.mustache_section_end, $.mustache_erroneous_section_end),
85
+ ),
86
+
87
+ mustache_section_begin: ($) =>
88
+ seq('{{#', alias($._mustache_start_tag_name, $.mustache_tag_name), '}}'),
89
+
90
+ mustache_section_end: ($) =>
91
+ seq('{{/', alias($._mustache_end_tag_name, $.mustache_tag_name), '}}'),
92
+
93
+ mustache_erroneous_section_end: ($) =>
94
+ seq(
95
+ '{{/',
96
+ alias(
97
+ $._mustache_erroneous_end_tag_name,
98
+ $.mustache_erroneous_tag_name,
99
+ ),
100
+ '}}',
101
+ ),
102
+
103
+ mustache_inverted_section: ($) =>
104
+ seq(
105
+ $.mustache_inverted_section_begin,
106
+ repeat($._node),
107
+ choice(
108
+ $.mustache_inverted_section_end,
109
+ $.mustache_erroneous_inverted_section_end,
110
+ ),
111
+ ),
112
+
113
+ mustache_inverted_section_begin: ($) =>
114
+ seq('{{^', alias($._mustache_start_tag_name, $.mustache_tag_name), '}}'),
115
+
116
+ mustache_inverted_section_end: ($) =>
117
+ seq('{{/', alias($._mustache_end_tag_name, $.mustache_tag_name), '}}'),
118
+
119
+ mustache_erroneous_inverted_section_end: ($) =>
120
+ seq(
121
+ '{{/',
122
+ alias(
123
+ $._mustache_erroneous_end_tag_name,
124
+ $.mustache_erroneous_tag_name,
125
+ ),
126
+ '}}',
127
+ ),
128
+
129
+ _mustache_expression: ($) =>
130
+ choice($.mustache_path_expression, $.mustache_identifier, '.'),
131
+
132
+ mustache_identifier: ($) => /[a-zA-Z0-9_-]+/,
133
+
134
+ mustache_path_expression: ($) =>
135
+ seq($.mustache_identifier, repeat1(seq('.', $.mustache_identifier))),
136
+
137
+ html_element: ($) =>
138
+ choice(
139
+ seq(
140
+ $.html_start_tag,
141
+ repeat($._node), // 0 or more times
142
+ choice(
143
+ $.html_end_tag,
144
+ $._html_implicit_end_tag,
145
+ alias(
146
+ $._mustache_end_tag_html_implicit_end_tag,
147
+ $.html_forced_end_tag,
148
+ ),
149
+ ),
150
+ ),
151
+ $.html_self_closing_tag,
152
+ ),
153
+
154
+ html_script_element: ($) =>
155
+ seq(
156
+ alias($.html_script_start_tag, $.html_start_tag),
157
+ optional($.html_raw_text),
158
+ $.html_end_tag,
159
+ ),
160
+
161
+ html_style_element: ($) =>
162
+ seq(
163
+ alias($.html_style_start_tag, $.html_start_tag),
164
+ optional($.html_raw_text),
165
+ $.html_end_tag,
166
+ ),
167
+
168
+ html_raw_element: ($) =>
169
+ seq(
170
+ alias($.html_raw_start_tag, $.html_start_tag),
171
+ optional($.html_raw_text),
172
+ $.html_end_tag,
173
+ ),
174
+
175
+ html_start_tag: ($) =>
176
+ seq(
177
+ '<',
178
+ alias($._html_start_tag_name, $.html_tag_name),
179
+ repeat($._attribute),
180
+ '>',
181
+ ),
182
+
183
+ html_script_start_tag: ($) =>
184
+ seq(
185
+ '<',
186
+ alias($._html_script_start_tag_name, $.html_tag_name),
187
+ repeat($._attribute),
188
+ '>',
189
+ ),
190
+
191
+ html_style_start_tag: ($) =>
192
+ seq(
193
+ '<',
194
+ alias($._html_style_start_tag_name, $.html_tag_name),
195
+ repeat($._attribute),
196
+ '>',
197
+ ),
198
+
199
+ html_raw_start_tag: ($) =>
200
+ seq(
201
+ '<',
202
+ alias($._html_raw_start_tag_name, $.html_tag_name),
203
+ repeat($._attribute),
204
+ '>',
205
+ ),
206
+
207
+ html_self_closing_tag: ($) =>
208
+ seq(
209
+ '<',
210
+ alias($._html_start_tag_name, $.html_tag_name),
211
+ repeat($._attribute),
212
+ '/>',
213
+ ),
214
+
215
+ html_end_tag: ($) =>
216
+ seq('</', alias($._html_end_tag_name, $.html_tag_name), '>'),
217
+
218
+ html_erroneous_end_tag: ($) =>
219
+ seq('</', $.html_erroneous_end_tag_name, '>'),
220
+
221
+ _attribute: ($) => choice($.mustache_attribute, $.html_attribute),
222
+ html_attribute: ($) =>
223
+ seq(
224
+ seq(
225
+ $.html_attribute_name,
226
+ optional(
227
+ seq(
228
+ '=',
229
+ choice(
230
+ $.html_attribute_value,
231
+ $.html_quoted_attribute_value,
232
+ $.mustache_interpolation,
233
+ ),
234
+ ),
235
+ ),
236
+ ),
237
+ ),
238
+
239
+ mustache_attribute: ($) =>
240
+ choice(
241
+ alias(
242
+ $.mustache_inverted_section_attribute,
243
+ $.mustache_inverted_section,
244
+ ),
245
+ alias($.mustache_section_attribute, $.mustache_section),
246
+ ),
247
+
248
+ mustache_inverted_section_attribute: ($) =>
249
+ seq(
250
+ $.mustache_inverted_section_begin,
251
+ repeat1($._attribute),
252
+ $.mustache_inverted_section_end,
253
+ ),
254
+ mustache_section_attribute: ($) =>
255
+ seq(
256
+ $.mustache_section_begin,
257
+ repeat1($._attribute),
258
+ $.mustache_section_end,
259
+ ),
260
+
261
+ html_attribute_name: (_) => /[^<>{}"'/=\s]+/,
262
+
263
+ html_attribute_value: (_) => /[^<>"{}'=\s]+/,
264
+
265
+ // An entity can be named, numeric (decimal), or numeric (hexacecimal). The
266
+ // longest entity name is 29 characters long, and the HTML spec says that
267
+ // no more will ever be added.
268
+ html_entity: (_) =>
269
+ /&(#([xX][0-9a-fA-F]{1,6}|[0-9]{1,5})|[A-Za-z]{1,30});?/,
270
+
271
+ _html_attribute_value_no_single_quote: ($) => /[^'{}]+/,
272
+ _html_attribute_value_no_double_quote: ($) => /[^"{}]+/,
273
+ // Single braces that aren't part of {{ or }}
274
+ _single_curly_brace: ($) => /[{}]/,
275
+ _attribute_value_no_double_quote: ($) =>
276
+ choice(
277
+ $._mustache_node,
278
+ alias($._html_attribute_value_no_single_quote, $.text),
279
+ ),
280
+ _attribute_value_no_single_quote: ($) =>
281
+ choice(
282
+ $._mustache_node,
283
+ alias($._html_attribute_value_no_double_quote, $.text),
284
+ ),
285
+ _mustache_section_no_single_quote: ($) =>
286
+ seq(
287
+ $.mustache_section_begin,
288
+ repeat(
289
+ alias(
290
+ $._attribute_value_no_single_quote,
291
+ $._mustache_section_content,
292
+ ),
293
+ ),
294
+ $.mustache_section_end,
295
+ ),
296
+ _mustache_section_no_double_quote: ($) =>
297
+ seq(
298
+ $.mustache_section_begin,
299
+ repeat(
300
+ alias(
301
+ $._attribute_value_no_double_quote,
302
+ '_mustache_section_content',
303
+ ),
304
+ ),
305
+ $.mustache_section_end,
306
+ ),
307
+ _mustache_inverted_section_no_single_quote: ($) =>
308
+ seq(
309
+ $.mustache_inverted_section_begin,
310
+ repeat(
311
+ alias(
312
+ $._attribute_value_no_single_quote,
313
+ $._mustache_inverted_section_content,
314
+ ),
315
+ ),
316
+ $.mustache_inverted_section_end,
317
+ ),
318
+ _mustache_inverted_section_no_double_quote: ($) =>
319
+ seq(
320
+ $.mustache_inverted_section_begin,
321
+ repeat(
322
+ alias(
323
+ $._attribute_value_no_double_quote,
324
+ $._mustache_inverted_section_content,
325
+ ),
326
+ ),
327
+ $.mustache_inverted_section_end,
328
+ ),
329
+ _mustache_comment_no_single_quote: ($) =>
330
+ seq(
331
+ '{{!',
332
+ alias(
333
+ $._html_attribute_value_no_single_quote,
334
+ $._mustache_comment_content,
335
+ ),
336
+ '}}',
337
+ ),
338
+ _mustache_comment_no_double_quote: ($) =>
339
+ seq(
340
+ '{{!',
341
+ alias(
342
+ $._html_attribute_value_no_double_quote,
343
+ $._mustache_comment_content,
344
+ ),
345
+ '}}',
346
+ ),
347
+ _mustache_partial_no_single_quote: ($) =>
348
+ seq(
349
+ '{{>',
350
+ alias(
351
+ $._html_attribute_value_no_single_quote,
352
+ $._mustache_partial_content,
353
+ ),
354
+ '}}',
355
+ ),
356
+ _mustache_partial_no_double_quote: ($) =>
357
+ seq(
358
+ '{{>',
359
+ alias(
360
+ $._html_attribute_value_no_double_quote,
361
+ $._mustache_partial_content,
362
+ ),
363
+ '}}',
364
+ ),
365
+ _mustache_node_no_single_quote: ($) =>
366
+ choice(
367
+ $.mustache_interpolation,
368
+ alias($._mustache_comment_no_single_quote, $.mustache_comment),
369
+ alias($._mustache_partial_no_single_quote, $.mustache_partial),
370
+ alias($._mustache_section_no_single_quote, $.mustache_section),
371
+ alias(
372
+ $._mustache_inverted_section_no_single_quote,
373
+ $.mustache_inverted_section,
374
+ ),
375
+ ),
376
+ _mustache_node_no_double_quote: ($) =>
377
+ choice(
378
+ $.mustache_interpolation,
379
+ alias($._mustache_comment_no_double_quote, $.mustache_comment),
380
+ alias($._mustache_partial_no_double_quote, $.mustache_partial),
381
+ alias($._mustache_section_no_double_quote, $.mustache_section),
382
+ alias(
383
+ $._mustache_inverted_section_no_double_quote,
384
+ $.mustache_inverted_section,
385
+ ),
386
+ ),
387
+
388
+ html_quoted_attribute_value: ($) =>
389
+ choice(
390
+ seq(
391
+ "'",
392
+ repeat(
393
+ choice(
394
+ alias(
395
+ $._html_attribute_value_no_single_quote,
396
+ $.html_attribute_value,
397
+ ),
398
+ $._mustache_node_no_single_quote,
399
+ alias($._single_curly_brace, $.html_attribute_value),
400
+ ),
401
+ ),
402
+ "'",
403
+ ),
404
+ seq(
405
+ '"',
406
+ repeat(
407
+ choice(
408
+ alias(
409
+ $._html_attribute_value_no_double_quote,
410
+ $.html_attribute_value,
411
+ ),
412
+ $._mustache_node_no_double_quote,
413
+ alias($._single_curly_brace, $.html_attribute_value),
414
+ ),
415
+ ),
416
+ '"',
417
+ ),
418
+ ),
419
+
420
+ // Text content - allows > (valid in HTML text) but excludes { and } for mustache handling
421
+ text: (_) => /[^<{}&\s]([^<{}&]*[^<{}&\s])?/,
422
+ // Single braces that aren't part of {{ or }} in text content
423
+ _text_brace: (_) => /[{}]/,
424
+ _text_ampersand: (_) => '&',
425
+ },
426
+ });
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@reteps/tree-sitter-htmlmustache",
3
+ "version": "0.0.18",
4
+ "description": "HTML with Mustache/Handlebars template syntax grammar for tree-sitter",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/reteps/tree-sitter-htmlmustache.git"
8
+ },
9
+ "license": "MIT",
10
+ "author": {
11
+ "name": "Peter Stenger"
12
+ },
13
+ "main": "bindings/node",
14
+ "bin": {
15
+ "htmlmustache": "cli/out/check.js"
16
+ },
17
+ "types": "bindings/node",
18
+ "keywords": [
19
+ "incremental",
20
+ "parsing",
21
+ "tree-sitter",
22
+ "html",
23
+ "mustache",
24
+ "handlebars"
25
+ ],
26
+ "files": [
27
+ "grammar.js",
28
+ "tree-sitter.json",
29
+ "binding.gyp",
30
+ "prebuilds/**",
31
+ "bindings/node/*",
32
+ "queries/*",
33
+ "src/**",
34
+ "*.wasm",
35
+ "cli/out/*"
36
+ ],
37
+ "dependencies": {
38
+ "chalk": "^4.1.2",
39
+ "node-addon-api": "^8.2.2",
40
+ "node-gyp-build": "^4.8.2"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.19.11",
44
+ "eslint": "^9.14.0",
45
+ "eslint-config-treesitter": "^1.0.2",
46
+ "prebuildify": "^6.0.1",
47
+ "prettier": "^3.8.1",
48
+ "tree-sitter": "^0.25.0",
49
+ "tree-sitter-cli": "^0.26.3",
50
+ "typescript": "^5.7.0",
51
+ "vitest": "^3.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "tree-sitter": "^0.25.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "tree-sitter": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "pnpm": {
62
+ "onlyBuiltDependencies": [
63
+ "esbuild",
64
+ "tree-sitter",
65
+ "tree-sitter-cli"
66
+ ]
67
+ },
68
+ "scripts": {
69
+ "install": "node-gyp-build",
70
+ "build": "tree-sitter build --wasm",
71
+ "prepack": "tsc -p cli/tsconfig.json",
72
+ "build:cli": "tsc -p cli/tsconfig.json",
73
+ "check": "node cli/out/check.js check",
74
+ "lint": "eslint .",
75
+ "format": "prettier --write .",
76
+ "format:check": "prettier --check .",
77
+ "prestart": "tree-sitter build --wasm",
78
+ "start": "tree-sitter playground",
79
+ "test": "tree-sitter test && node --test bindings/node/*_test.js",
80
+ "test:cli": "vitest run --config cli/vitest.config.ts"
81
+ }
82
+ }
@@ -0,0 +1,30 @@
1
+ ; HTML
2
+ (html_tag_name) @tag
3
+ (html_erroneous_end_tag_name) @tag.error
4
+ (html_doctype) @constant
5
+ (html_attribute_name) @attribute
6
+ (html_attribute_value) @string
7
+ (html_comment) @comment
8
+
9
+ [
10
+ "<"
11
+ ">"
12
+ "</"
13
+ "/>"
14
+ ] @punctuation.bracket
15
+
16
+ ; Mustache
17
+ (mustache_tag_name) @variable
18
+ (mustache_identifier) @variable
19
+ (mustache_comment) @comment
20
+
21
+ [
22
+ "{{"
23
+ "}}"
24
+ "{{{"
25
+ "}}}"
26
+ "{{>"
27
+ "{{#"
28
+ "{{/"
29
+ "{{^"
30
+ ] @keyword
@@ -0,0 +1,7 @@
1
+ ((html_script_element
2
+ (html_raw_text) @injection.content)
3
+ (#set! injection.language "javascript"))
4
+
5
+ ((html_style_element
6
+ (html_raw_text) @injection.content)
7
+ (#set! injection.language "css"))
@@ -0,0 +1,3 @@
1
+ // Custom raw text tags - content inside these elements is not parsed as HTML/Mustache.
2
+ // Uncomment and edit to add custom raw tags:
3
+ #define CUSTOM_RAW_TAGS "MARKDOWN"