@reteps/tree-sitter-htmlmustache 0.0.30 → 0.0.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reteps/tree-sitter-htmlmustache",
3
- "version": "0.0.30",
3
+ "version": "0.0.34",
4
4
  "description": "HTML with Mustache/Handlebars template syntax grammar for tree-sitter",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "main": "bindings/node",
14
14
  "bin": {
15
- "htmlmustache": "cli/out/check.js"
15
+ "htmlmustache": "cli/out/main.js"
16
16
  },
17
17
  "types": "bindings/node",
18
18
  "keywords": [
@@ -36,12 +36,17 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "chalk": "^4.1.2",
39
+ "editorconfig": "^2.0.1",
39
40
  "node-addon-api": "^8.2.2",
40
41
  "node-gyp-build": "^4.8.2",
41
- "tree-sitter": "^0.25.0"
42
+ "tree-sitter": "^0.25.0",
43
+ "vscode-languageserver-textdocument": "^1.0.12",
44
+ "web-tree-sitter": "^0.25.0"
42
45
  },
43
46
  "devDependencies": {
47
+ "@eslint/js": "^9.39.2",
44
48
  "@types/node": "^22.19.11",
49
+ "esbuild": "^0.25.0",
45
50
  "eslint": "^9.14.0",
46
51
  "eslint-config-treesitter": "^1.0.2",
47
52
  "prebuildify": "^6.0.1",
@@ -49,6 +54,7 @@
49
54
  "tree-sitter": "^0.25.0",
50
55
  "tree-sitter-cli": "^0.26.3",
51
56
  "typescript": "^5.7.0",
57
+ "typescript-eslint": "^8.54.0",
52
58
  "vitest": "^3.0.0"
53
59
  },
54
60
  "pnpm": {
@@ -61,8 +67,8 @@
61
67
  "scripts": {
62
68
  "install": "node-gyp-build",
63
69
  "build": "tree-sitter build --wasm",
64
- "prepack": "tsc -p cli/tsconfig.json",
65
- "build:cli": "tsc -p cli/tsconfig.json",
70
+ "prepack": "node cli/esbuild.mjs",
71
+ "build:cli": "node cli/esbuild.mjs",
66
72
  "check": "node cli/out/check.js check",
67
73
  "lint": "eslint .",
68
74
  "format": "prettier --write .",
package/cli/out/check.js DELETED
@@ -1,235 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.collectErrors = collectErrors;
8
- exports.formatError = formatError;
9
- exports.formatSummary = formatSummary;
10
- exports.expandGlobs = expandGlobs;
11
- exports.run = run;
12
- const node_fs_1 = __importDefault(require("node:fs"));
13
- const node_path_1 = __importDefault(require("node:path"));
14
- const chalk_1 = __importDefault(require("chalk"));
15
- function loadParser() {
16
- let Parser;
17
- try {
18
- Parser = require('tree-sitter');
19
- }
20
- catch {
21
- console.error(chalk_1.default.red('Error: tree-sitter is not installed.\n') +
22
- 'Install it with: npm install tree-sitter');
23
- process.exit(1);
24
- }
25
- let language;
26
- try {
27
- language = require(node_path_1.default.resolve(__dirname, '..', '..'));
28
- }
29
- catch {
30
- console.error(chalk_1.default.red('Error: could not load tree-sitter-htmlmustache bindings.\n') +
31
- 'Run "npm install" in the tree-sitter-htmlmustache root first.');
32
- process.exit(1);
33
- }
34
- const parser = new Parser();
35
- parser.setLanguage(language);
36
- return parser;
37
- }
38
- // ── Error collection ──
39
- function errorMessageForNode(nodeType, node) {
40
- if (nodeType === 'mustache_erroneous_section_end' || nodeType === 'mustache_erroneous_inverted_section_end') {
41
- const tagNameNode = node.children.find((c) => c.type === 'mustache_erroneous_tag_name');
42
- return `Mismatched mustache section: {{/${tagNameNode?.text || '?'}}}`;
43
- }
44
- if (nodeType === 'html_erroneous_end_tag') {
45
- const tagNameNode = node.children.find((c) => c.type === 'html_erroneous_end_tag_name');
46
- return `Mismatched HTML end tag: </${tagNameNode?.text || '?'}>`;
47
- }
48
- if (nodeType === 'ERROR') {
49
- return 'Syntax error';
50
- }
51
- // isMissing node
52
- return `Missing ${nodeType}`;
53
- }
54
- const ERROR_NODE_TYPES = new Set([
55
- 'ERROR',
56
- 'mustache_erroneous_section_end',
57
- 'mustache_erroneous_inverted_section_end',
58
- 'html_erroneous_end_tag',
59
- ]);
60
- function collectErrors(tree, file) {
61
- const errors = [];
62
- const cursor = tree.walk();
63
- function visit() {
64
- const node = cursor.currentNode;
65
- const nodeType = cursor.nodeType;
66
- if (ERROR_NODE_TYPES.has(nodeType) || cursor.nodeIsMissing) {
67
- errors.push({
68
- file,
69
- line: node.startPosition.row + 1,
70
- column: node.startPosition.column + 1,
71
- endLine: node.endPosition.row + 1,
72
- endColumn: node.endPosition.column + 1,
73
- message: errorMessageForNode(nodeType, node),
74
- nodeText: node.text,
75
- });
76
- // Don't recurse into ERROR nodes — the children are not meaningful
77
- if (nodeType === 'ERROR')
78
- return;
79
- }
80
- if (cursor.gotoFirstChild()) {
81
- do {
82
- visit();
83
- } while (cursor.gotoNextSibling());
84
- cursor.gotoParent();
85
- }
86
- }
87
- visit();
88
- return errors;
89
- }
90
- // ── Formatting ──
91
- function formatError(error, source) {
92
- const lines = source.split('\n');
93
- const errorLine = error.line - 1; // 0-based index
94
- // Location header
95
- const header = chalk_1.default.bold(`${error.file}:${error.line}:${error.column}`) +
96
- ' ' + chalk_1.default.red('error') + ': ' + error.message;
97
- // Context lines: up to 2 before + the error line(s)
98
- const contextStart = Math.max(0, errorLine - 2);
99
- const contextEnd = Math.min(lines.length - 1, error.endLine - 1);
100
- const gutterWidth = String(contextEnd + 1).length;
101
- const pad = (n) => String(n).padStart(gutterWidth);
102
- const outputLines = [header];
103
- outputLines.push(chalk_1.default.dim(' '.repeat(gutterWidth) + ' |'));
104
- for (let i = contextStart; i <= contextEnd; i++) {
105
- const lineNum = i + 1;
106
- outputLines.push(chalk_1.default.dim(`${pad(lineNum)} |`) + ' ' + lines[i]);
107
- }
108
- // Underline: only on the last displayed line of the error
109
- const lastErrorLineIdx = error.endLine - 1;
110
- const lastLine = lines[lastErrorLineIdx] || '';
111
- let underlineStart;
112
- let underlineEnd;
113
- if (error.line === error.endLine) {
114
- // Single-line error
115
- underlineStart = error.column - 1;
116
- underlineEnd = error.endColumn - 1;
117
- }
118
- else {
119
- // Multi-line: underline to end of last line
120
- underlineStart = 0;
121
- underlineEnd = lastLine.length;
122
- }
123
- const underlineLength = Math.max(1, underlineEnd - underlineStart);
124
- const underline = ' '.repeat(underlineStart) + '^'.repeat(underlineLength) +
125
- ' ' + error.message;
126
- outputLines.push(chalk_1.default.dim(' '.repeat(gutterWidth) + ' |') + ' ' + chalk_1.default.red(underline));
127
- return outputLines.join('\n');
128
- }
129
- function formatSummary(totalErrors, filesWithErrors, totalFiles) {
130
- if (totalErrors === 0) {
131
- return chalk_1.default.green(`No errors found (${totalFiles} ${totalFiles === 1 ? 'file' : 'files'} checked)`);
132
- }
133
- const errStr = totalErrors === 1 ? 'error' : 'errors';
134
- const errFileStr = filesWithErrors === 1 ? 'file' : 'files';
135
- const totalStr = totalFiles === 1 ? 'file' : 'files';
136
- return chalk_1.default.red(`${totalErrors} ${errStr} in ${filesWithErrors} ${errFileStr}`) +
137
- ` (${totalFiles} ${totalStr} checked)`;
138
- }
139
- // ── Glob expansion ──
140
- function expandGlobs(patterns) {
141
- const files = new Set();
142
- const cwd = process.cwd();
143
- for (const pattern of patterns) {
144
- // If the pattern is an exact file path, use it directly
145
- if (!pattern.includes('*') && !pattern.includes('?')) {
146
- const resolved = node_path_1.default.resolve(cwd, pattern);
147
- if (node_fs_1.default.existsSync(resolved)) {
148
- files.add(resolved);
149
- }
150
- }
151
- else {
152
- for (const match of node_fs_1.default.globSync(pattern, { cwd })) {
153
- files.add(node_path_1.default.resolve(cwd, match));
154
- }
155
- }
156
- }
157
- return [...files].sort();
158
- }
159
- // ── Main ──
160
- const USAGE = `Usage: htmlmustache check <patterns...>
161
-
162
- Check HTML Mustache templates for errors.
163
-
164
- Arguments:
165
- patterns One or more glob patterns (e.g. '**/*.mustache' '**/*.hbs')
166
-
167
- Options:
168
- --help Show this help message
169
-
170
- Examples:
171
- htmlmustache check '**/*.mustache'
172
- htmlmustache check 'templates/**/*.hbs' 'partials/**/*.mustache'`;
173
- function run(args) {
174
- // Strip "check" subcommand if present
175
- if (args[0] === 'check') {
176
- args = args.slice(1);
177
- }
178
- if (args.includes('--help') || args.length === 0) {
179
- console.log(USAGE);
180
- return args.includes('--help') ? 0 : 1;
181
- }
182
- const files = expandGlobs(args);
183
- if (files.length === 0) {
184
- console.error(chalk_1.default.yellow('No files matched the given patterns:'));
185
- for (const arg of args) {
186
- console.error(chalk_1.default.yellow(` ${arg}`));
187
- }
188
- return 1;
189
- }
190
- const parser = loadParser();
191
- let totalErrors = 0;
192
- let filesWithErrors = 0;
193
- const cwd = process.cwd();
194
- const errorOutput = [];
195
- for (const file of files) {
196
- const displayPath = node_path_1.default.relative(cwd, file) || file;
197
- const source = node_fs_1.default.readFileSync(file, 'utf-8');
198
- const tree = parser.parse(source);
199
- const errors = collectErrors(tree, displayPath);
200
- if (errors.length > 0) {
201
- filesWithErrors++;
202
- totalErrors += errors.length;
203
- for (const error of errors) {
204
- errorOutput.push(formatError(error, source));
205
- }
206
- }
207
- console.log(errors.length > 0
208
- ? chalk_1.default.red(displayPath)
209
- : chalk_1.default.dim(displayPath));
210
- }
211
- if (errorOutput.length > 0) {
212
- console.log();
213
- for (const output of errorOutput) {
214
- console.log(output);
215
- console.log();
216
- }
217
- }
218
- console.log(formatSummary(totalErrors, filesWithErrors, files.length));
219
- return totalErrors > 0 ? 1 : 0;
220
- }
221
- // CLI entry point
222
- if (require.main === module) {
223
- const args = process.argv.slice(2);
224
- if (args[0] !== 'check' && !args.includes('--help')) {
225
- if (args.length === 0) {
226
- console.log(USAGE);
227
- process.exit(1);
228
- }
229
- console.error(chalk_1.default.red(`Unknown command: ${args[0]}`));
230
- console.error('Run "htmlmustache --help" for usage.');
231
- process.exit(1);
232
- }
233
- const code = run(args);
234
- process.exit(code);
235
- }