@emnudge/wat-lsp 0.3.0 → 0.4.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.
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * wat-check-wasm: A standalone CLI tool for scanning WAT files for issues
5
+ *
6
+ * This tool uses the WASM build of wat-lsp to provide file validation.
7
+ * It mirrors the functionality of the native wat-check binary.
8
+ */
9
+
10
+ import { readFileSync, existsSync } from 'node:fs';
11
+ import { resolve, dirname, join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { Parser } from 'web-tree-sitter';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ // Parse command line arguments
19
+ function parseArgs(args) {
20
+ const result = {
21
+ files: [],
22
+ format: 'text',
23
+ errorsOnly: false,
24
+ quiet: false,
25
+ help: false,
26
+ };
27
+
28
+ let i = 0;
29
+ while (i < args.length) {
30
+ const arg = args[i];
31
+
32
+ if (arg === '-h' || arg === '--help') {
33
+ result.help = true;
34
+ } else if (arg === '-f' || arg === '--format') {
35
+ i++;
36
+ result.format = args[i] || 'text';
37
+ } else if (arg === '-e' || arg === '--errors-only') {
38
+ result.errorsOnly = true;
39
+ } else if (arg === '-q' || arg === '--quiet') {
40
+ result.quiet = true;
41
+ } else if (arg === '-') {
42
+ // Special case: '-' means stdin
43
+ result.files.push(arg);
44
+ } else if (!arg.startsWith('-')) {
45
+ result.files.push(arg);
46
+ } else {
47
+ console.error(`Unknown option: ${arg}`);
48
+ process.exit(1);
49
+ }
50
+ i++;
51
+ }
52
+
53
+ return result;
54
+ }
55
+
56
+ function printHelp() {
57
+ console.log(`wat-check-wasm - WAT file checker using WASM build
58
+
59
+ Usage: wat-check-wasm [OPTIONS] <FILES>...
60
+
61
+ Arguments:
62
+ <FILES> Files to check. Use '-' to read from stdin.
63
+
64
+ Options:
65
+ -f, --format <FORMAT> Output format: text, json, compact [default: text]
66
+ -e, --errors-only Only show errors (hide warnings and hints)
67
+ -q, --quiet Suppress all output except errors (for scripting)
68
+ -h, --help Print help
69
+ `);
70
+ }
71
+
72
+ function severityToString(severity) {
73
+ switch (severity) {
74
+ case 1:
75
+ return 'error';
76
+ case 2:
77
+ return 'warning';
78
+ case 3:
79
+ return 'info';
80
+ case 4:
81
+ return 'hint';
82
+ default:
83
+ return 'unknown';
84
+ }
85
+ }
86
+
87
+ function severityToCompact(severity) {
88
+ switch (severity) {
89
+ case 1:
90
+ return 'E';
91
+ case 2:
92
+ return 'W';
93
+ case 3:
94
+ return 'I';
95
+ case 4:
96
+ return 'H';
97
+ default:
98
+ return '?';
99
+ }
100
+ }
101
+
102
+ function printDiagnosticsText(filename, diagnostics) {
103
+ for (const d of diagnostics) {
104
+ const line = d.range.start.line + 1;
105
+ const col = d.range.start.character + 1;
106
+ const severity = severityToString(d.severity);
107
+ console.log(`${filename}:${line}:${col}: ${severity}: ${d.message}`);
108
+ }
109
+ }
110
+
111
+ function printDiagnosticsCompact(filename, diagnostics) {
112
+ for (const d of diagnostics) {
113
+ const line = d.range.start.line + 1;
114
+ const col = d.range.start.character + 1;
115
+ const severity = severityToCompact(d.severity);
116
+ console.log(`${filename}:${line}:${col}:${severity}:${d.message}`);
117
+ }
118
+ }
119
+
120
+ async function readStdin() {
121
+ const chunks = [];
122
+ for await (const chunk of process.stdin) {
123
+ chunks.push(chunk);
124
+ }
125
+ return Buffer.concat(chunks).toString('utf8');
126
+ }
127
+
128
+ /**
129
+ * Initialize WatLSP for Node.js environment
130
+ */
131
+ async function initWatLSP() {
132
+ // Paths to WASM files
133
+ const distWasmDir = join(__dirname, '..', 'dist', 'wasm');
134
+ const treeSitterWasmPath = join(distWasmDir, 'tree-sitter.wasm');
135
+ const watLspWasmPath = join(distWasmDir, 'wat_lsp_rust_bg.wasm');
136
+
137
+ // Initialize web-tree-sitter with file:// URL for Node.js
138
+ await Parser.init({
139
+ locateFile: (file) => {
140
+ if (file === 'tree-sitter.wasm') {
141
+ return `file://${treeSitterWasmPath}`;
142
+ }
143
+ return file;
144
+ },
145
+ });
146
+
147
+ // Load the wat-lsp WASM module
148
+ const { default: initWasm, WatLSP } = await import('../dist/wasm/wat_lsp_rust.js');
149
+
150
+ // In Node.js, we need to pass the WASM file as a buffer or URL
151
+ const wasmBuffer = readFileSync(watLspWasmPath);
152
+ await initWasm(wasmBuffer);
153
+
154
+ // Create and initialize the LSP instance
155
+ const lsp = new WatLSP();
156
+ const success = await lsp.initialize();
157
+
158
+ if (!success) {
159
+ throw new Error('Failed to initialize WAT LSP');
160
+ }
161
+
162
+ return lsp;
163
+ }
164
+
165
+ async function main() {
166
+ const args = parseArgs(process.argv.slice(2));
167
+
168
+ if (args.help) {
169
+ printHelp();
170
+ process.exit(0);
171
+ }
172
+
173
+ if (args.files.length === 0) {
174
+ console.error('Error: No files specified');
175
+ printHelp();
176
+ process.exit(1);
177
+ }
178
+
179
+ // Initialize the WASM LSP
180
+ let lsp;
181
+ try {
182
+ lsp = await initWatLSP();
183
+ } catch (e) {
184
+ console.error(`Failed to initialize WASM LSP: ${e.message}`);
185
+ process.exit(1);
186
+ }
187
+
188
+ const allResults = [];
189
+ let totalErrors = 0;
190
+ let totalWarnings = 0;
191
+ let hadReadError = false;
192
+
193
+ for (const path of args.files) {
194
+ let filename;
195
+ let source;
196
+
197
+ if (path === '-') {
198
+ // Read from stdin
199
+ try {
200
+ source = await readStdin();
201
+ filename = '<stdin>';
202
+ } catch (e) {
203
+ console.error(`stdin: Failed to read: ${e.message}`);
204
+ hadReadError = true;
205
+ continue;
206
+ }
207
+ } else {
208
+ // Read from file
209
+ const resolvedPath = resolve(path);
210
+ if (!existsSync(resolvedPath)) {
211
+ console.error(`${path}: File not found`);
212
+ hadReadError = true;
213
+ continue;
214
+ }
215
+ try {
216
+ source = readFileSync(resolvedPath, 'utf8');
217
+ filename = path;
218
+ } catch (e) {
219
+ console.error(`${path}: Failed to read: ${e.message}`);
220
+ hadReadError = true;
221
+ continue;
222
+ }
223
+ }
224
+
225
+ // Parse and get diagnostics
226
+ lsp.parse(source);
227
+ let diagnostics = lsp.provideDiagnostics();
228
+
229
+ // Convert to array if needed (JsValue might be array-like)
230
+ diagnostics = Array.from(diagnostics);
231
+
232
+ // Filter errors only if requested
233
+ if (args.errorsOnly) {
234
+ diagnostics = diagnostics.filter((d) => d.severity === 1);
235
+ }
236
+
237
+ const errorCount = diagnostics.filter((d) => d.severity === 1).length;
238
+ const warningCount = diagnostics.filter((d) => d.severity === 2).length;
239
+
240
+ totalErrors += errorCount;
241
+ totalWarnings += warningCount;
242
+
243
+ switch (args.format) {
244
+ case 'text':
245
+ if (diagnostics.length > 0) {
246
+ printDiagnosticsText(filename, diagnostics);
247
+ }
248
+ break;
249
+ case 'compact':
250
+ printDiagnosticsCompact(filename, diagnostics);
251
+ break;
252
+ case 'json':
253
+ allResults.push({
254
+ file: filename,
255
+ diagnostics: diagnostics.map((d) => ({
256
+ line: d.range.start.line + 1,
257
+ column: d.range.start.character + 1,
258
+ end_line: d.range.end.line + 1,
259
+ end_column: d.range.end.character + 1,
260
+ severity: severityToString(d.severity),
261
+ message: d.message,
262
+ })),
263
+ error_count: errorCount,
264
+ warning_count: warningCount,
265
+ });
266
+ break;
267
+ }
268
+ }
269
+
270
+ // JSON output at the end
271
+ if (args.format === 'json') {
272
+ const output = {
273
+ files: allResults,
274
+ summary: {
275
+ total_errors: totalErrors,
276
+ total_warnings: totalWarnings,
277
+ files_checked: args.files.length,
278
+ },
279
+ };
280
+ console.log(JSON.stringify(output, null, 2));
281
+ } else if (!args.quiet) {
282
+ // Summary for text output
283
+ if (args.files.length > 1 || totalErrors > 0 || totalWarnings > 0) {
284
+ const fileWord = args.files.length === 1 ? 'file' : 'files';
285
+ console.error(
286
+ `\nChecked ${args.files.length} ${fileWord}: ${totalErrors} error(s), ${totalWarnings} warning(s)`
287
+ );
288
+ }
289
+ }
290
+
291
+ // Exit code: 1 if errors found, 2 if read errors, 0 if clean
292
+ if (totalErrors > 0) {
293
+ process.exit(1);
294
+ } else if (hadReadError) {
295
+ process.exit(2);
296
+ } else {
297
+ process.exit(0);
298
+ }
299
+ }
300
+
301
+ main().catch((e) => {
302
+ console.error(`Fatal error: ${e.message}`);
303
+ process.exit(1);
304
+ });
@@ -30,7 +30,7 @@ export class WatLSP {
30
30
  */
31
31
  provideReferences(line: number, col: number, include_declaration: boolean): any;
32
32
  /**
33
- * Get diagnostics (syntax errors) for the current document
33
+ * Get diagnostics (syntax and semantic errors) for the current document
34
34
  */
35
35
  provideDiagnostics(): any;
36
36
  /**
@@ -320,7 +320,7 @@ export class WatLSP {
320
320
  return ret;
321
321
  }
322
322
  /**
323
- * Get diagnostics (syntax errors) for the current document
323
+ * Get diagnostics (syntax and semantic errors) for the current document
324
324
  * @returns {any}
325
325
  */
326
326
  provideDiagnostics() {
@@ -564,6 +564,10 @@ function __wbg_get_imports() {
564
564
  const ret = result;
565
565
  return ret;
566
566
  };
567
+ imports.wbg.__wbg_isMissing_bca0753051acc9d6 = function(arg0) {
568
+ const ret = arg0.isMissing;
569
+ return ret;
570
+ };
567
571
  imports.wbg.__wbg_length_22ac23eaec9d8053 = function(arg0) {
568
572
  const ret = arg0.length;
569
573
  return ret;
@@ -731,8 +735,8 @@ function __wbg_get_imports() {
731
735
  const ret = arg0;
732
736
  return ret;
733
737
  };
734
- imports.wbg.__wbindgen_cast_ef8bfe7054278247 = function(arg0, arg1) {
735
- // Cast intrinsic for `Closure(Closure { dtor_idx: 1393, function: Function { arguments: [Externref], shim_idx: 1394, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
738
+ imports.wbg.__wbindgen_cast_dd223879213a40a2 = function(arg0, arg1) {
739
+ // Cast intrinsic for `Closure(Closure { dtor_idx: 732, function: Function { arguments: [Externref], shim_idx: 733, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
736
740
  const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__hdb18b2b625d87f9d, wasm_bindgen__convert__closures_____invoke__hfee0e13a54da0cc9);
737
741
  return ret;
738
742
  };
Binary file
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@emnudge/wat-lsp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "WebAssembly Text Format (WAT) Language Server - WASM build for browser and Node.js",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "wat-check-wasm": "./bin/wat-check-wasm.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/index.d.ts",
@@ -16,7 +19,8 @@
16
19
  }
17
20
  },
18
21
  "files": [
19
- "dist"
22
+ "dist",
23
+ "bin"
20
24
  ],
21
25
  "scripts": {
22
26
  "build:wasm": "cd ../.. && wasm-pack build --target web --no-opt --no-pack -- --features wasm --no-default-features && mkdir -p packages/wat-lsp/dist/wasm && cp pkg/wat_lsp_rust.js pkg/wat_lsp_rust.d.ts pkg/wat_lsp_rust_bg.wasm packages/wat-lsp/dist/wasm/",