@rsvelte/language-server 0.2.1 → 0.3.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 CHANGED
@@ -18,7 +18,8 @@ toolchain.
18
18
  - **Diagnostics** — push diagnostics from the bundled
19
19
  [`rsvelte_lint`](https://github.com/baseballyama/rsvelte/tree/main/crates/rsvelte_lint)
20
20
  engine (compiled to wasm, vendored in the package — no extra install). Runs on
21
- open, on change (300 ms debounced), and on save.
21
+ open, on change (300 ms debounced), and on save. The rule set comes from the
22
+ project's own [`rsvelte-lint.json`](#lint-configuration).
22
23
 
23
24
  That's the full v1 surface — hover, completion, go-to-definition, rename,
24
25
  find-references, and TypeScript diagnostics are **not** implemented. Those
@@ -37,6 +38,38 @@ The server reads these from the client's `rsvelte.*` configuration:
37
38
  | `rsvelte.lint.enable` | `true` | Enable linting via the bundled engine. |
38
39
  | `rsvelte.rsvelteFmtPath` | `""` | Explicit path to a `rsvelte-fmt` binary (overrides resolution). |
39
40
 
41
+ ## Lint configuration
42
+
43
+ Severities and rule options come from a `rsvelte-lint.json` (or
44
+ `.rsvelte-lintrc.json`), discovered by walking up from the document's directory
45
+ — the same file, in the same order, that the
46
+ [`rsvelte-lint`](https://www.npmjs.com/package/@rsvelte/lint) CLI resolves, so
47
+ the editor reports what CI does. With no config file, every rule runs at its
48
+ default severity (the `recommended` preset); to start from nothing and opt in,
49
+ use `"extends": ["none"]`:
50
+
51
+ ```json
52
+ {
53
+ "extends": ["recommended"],
54
+ "rules": {
55
+ "svelte/no-at-html-tags": "error",
56
+ "svelte/no-unused-class-name": "off",
57
+ "svelte/button-has-type": ["warn", { "submit": true, "reset": false }]
58
+ }
59
+ }
60
+ ```
61
+
62
+ See the
63
+ [CLI's configuration reference](https://www.npmjs.com/package/@rsvelte/lint#configuration)
64
+ for the full shape. An ESLint config is deliberately **not** read: importing it
65
+ is opt-in on the CLI (`--config-from-eslint`), and a server that read it on its
66
+ own would report a different rule set than the same project's CI. A config that
67
+ can't be read or parsed is reported to the client's log and the recommended
68
+ preset is used, so a typo never leaves the editor without diagnostics.
69
+
70
+ Resolved configs are cached for the life of the server; they are dropped when a
71
+ config file is saved through the client, and on `workspace/didChangeConfiguration`.
72
+
40
73
  ## Usage
41
74
 
42
75
  ```sh
package/dist/server.mjs CHANGED
@@ -10114,7 +10114,7 @@ function getWellformedEdit(textEdit) {
10114
10114
 
10115
10115
  // src/server.ts
10116
10116
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10117
- import { dirname as dirname3 } from "node:path";
10117
+ import { dirname as dirname4 } from "node:path";
10118
10118
 
10119
10119
  // src/diagnostics.ts
10120
10120
  var import_node = __toESM(require_main3(), 1);
@@ -10233,11 +10233,11 @@ function load() {
10233
10233
  return null;
10234
10234
  }
10235
10235
  }
10236
- function runLint(source, filename) {
10236
+ function runLint(source, filename, config) {
10237
10237
  const m = load();
10238
10238
  if (!m) return null;
10239
10239
  try {
10240
- return m.lint(source, filename);
10240
+ return m.lint_with_config(source, filename, config);
10241
10241
  } catch {
10242
10242
  return null;
10243
10243
  }
@@ -10252,6 +10252,62 @@ function lintVersion() {
10252
10252
  }
10253
10253
  }
10254
10254
 
10255
+ // src/lintConfig.ts
10256
+ import { basename, dirname as dirname3, join as join3 } from "node:path";
10257
+ import { readFileSync } from "node:fs";
10258
+ var CONFIG_NAMES = ["rsvelte-lint.json", ".rsvelte-lintrc.json"];
10259
+ var NO_CONFIG = { text: "", path: null };
10260
+ var cache = /* @__PURE__ */ new Map();
10261
+ function isLintConfigPath(path) {
10262
+ return CONFIG_NAMES.includes(basename(path));
10263
+ }
10264
+ function clearLintConfigCache() {
10265
+ cache.clear();
10266
+ }
10267
+ function resolveLintConfig(dir, onError) {
10268
+ const cached = cache.get(dir);
10269
+ if (cached) return cached;
10270
+ const found = discover(dir);
10271
+ cache.set(dir, found.config);
10272
+ if (found.error) onError?.(`${found.config.path}: ${found.error}`);
10273
+ return found.config;
10274
+ }
10275
+ function discover(start) {
10276
+ let dir = start;
10277
+ for (; ; ) {
10278
+ for (const name of CONFIG_NAMES) {
10279
+ const found = read(join3(dir, name));
10280
+ if (found) return found;
10281
+ }
10282
+ const parent = dirname3(dir);
10283
+ if (parent === dir) return { config: NO_CONFIG, error: null };
10284
+ dir = parent;
10285
+ }
10286
+ }
10287
+ function read(path) {
10288
+ let text;
10289
+ try {
10290
+ text = readFileSync(path, "utf8");
10291
+ } catch (err) {
10292
+ const code = err.code;
10293
+ if (code === "ENOENT" || code === "ENOTDIR") return null;
10294
+ return { config: { text: "", path }, error: String(err) };
10295
+ }
10296
+ let parsed;
10297
+ try {
10298
+ parsed = JSON.parse(text);
10299
+ } catch (err) {
10300
+ return { config: { text: "", path }, error: String(err) };
10301
+ }
10302
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
10303
+ return {
10304
+ config: { text: "", path },
10305
+ error: "lint config must be a JSON object"
10306
+ };
10307
+ }
10308
+ return { config: { text, path }, error: null };
10309
+ }
10310
+
10255
10311
  // src/server.ts
10256
10312
  var DEFAULT_SETTINGS = {
10257
10313
  format: { enable: true },
@@ -10313,9 +10369,8 @@ async function refreshSettings() {
10313
10369
  }
10314
10370
  connection.onDidChangeConfiguration(async () => {
10315
10371
  await refreshSettings();
10316
- for (const doc of documents.all()) {
10317
- scheduleLint(doc, 0);
10318
- }
10372
+ clearLintConfigCache();
10373
+ relintOpenDocuments();
10319
10374
  });
10320
10375
  connection.onDocumentFormatting(async (params) => {
10321
10376
  if (!settings.format.enable) return [];
@@ -10323,7 +10378,7 @@ connection.onDocumentFormatting(async (params) => {
10323
10378
  if (!doc) return [];
10324
10379
  const fsPath = docFsPath(doc.uri);
10325
10380
  const binPath = resolveRsvelteFmt(
10326
- dirname3(fsPath),
10381
+ dirname4(fsPath),
10327
10382
  settings.rsvelteFmtPath
10328
10383
  );
10329
10384
  if (!binPath) return [];
@@ -10350,13 +10405,30 @@ async function lintDocument(doc) {
10350
10405
  connection.sendDiagnostics({ uri: doc.uri, diagnostics: [] });
10351
10406
  return;
10352
10407
  }
10353
- const json = runLint(doc.getText(), docFsPath(doc.uri));
10408
+ const fsPath = docFsPath(doc.uri);
10409
+ const config = resolveLintConfig(
10410
+ dirname4(fsPath),
10411
+ (message) => connection.console.warn(message)
10412
+ );
10413
+ const json = runLint(doc.getText(), fsPath, config.text);
10354
10414
  const diagnostics = json ? lintJsonToDiagnostics(json) : [];
10355
10415
  connection.sendDiagnostics({ uri: doc.uri, diagnostics });
10356
10416
  }
10417
+ function relintOpenDocuments() {
10418
+ for (const doc of documents.all()) {
10419
+ scheduleLint(doc, 0);
10420
+ }
10421
+ }
10357
10422
  documents.onDidOpen((e) => scheduleLint(e.document, 0));
10358
10423
  documents.onDidChangeContent((e) => scheduleLint(e.document));
10359
- documents.onDidSave((e) => scheduleLint(e.document, 0));
10424
+ documents.onDidSave((e) => {
10425
+ if (isLintConfigPath(docFsPath(e.document.uri))) {
10426
+ clearLintConfigCache();
10427
+ relintOpenDocuments();
10428
+ return;
10429
+ }
10430
+ scheduleLint(e.document, 0);
10431
+ });
10360
10432
  documents.onDidClose((e) => {
10361
10433
  const timer = debounceTimers.get(e.document.uri);
10362
10434
  if (timer) {
@@ -131,6 +131,41 @@ class ParseResultWasm {
131
131
  if (Symbol.dispose) ParseResultWasm.prototype[Symbol.dispose] = ParseResultWasm.prototype.free;
132
132
  exports.ParseResultWasm = ParseResultWasm;
133
133
 
134
+ /**
135
+ * Compile a Svelte component with the full compile-options object.
136
+ *
137
+ * Supports the function-form compile options (issue #1680): the `parametric`
138
+ * function forms of `customElement`/`css`/`runes`, a `warningFilter` callback,
139
+ * a constant `cssHashOverride`, and a dynamic `cssHash` callback. Returns the
140
+ * compile result as a JSON string (`{ js, css, warnings, metadata }`);
141
+ * callbacks are input-only. Throws on a parse failure, an invalid option, or a
142
+ * `cssHash` callback that throws.
143
+ * @param {string} source
144
+ * @param {any} options
145
+ * @returns {string}
146
+ */
147
+ function compile(source, options) {
148
+ let deferred3_0;
149
+ let deferred3_1;
150
+ try {
151
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
152
+ const len0 = WASM_VECTOR_LEN;
153
+ const ret = wasm.compile(ptr0, len0, options);
154
+ var ptr2 = ret[0];
155
+ var len2 = ret[1];
156
+ if (ret[3]) {
157
+ ptr2 = 0; len2 = 0;
158
+ throw takeFromExternrefTable0(ret[2]);
159
+ }
160
+ deferred3_0 = ptr2;
161
+ deferred3_1 = len2;
162
+ return getStringFromWasm0(ptr2, len2);
163
+ } finally {
164
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
165
+ }
166
+ }
167
+ exports.compile = compile;
168
+
134
169
  /**
135
170
  * Compile a Svelte component to client-side JavaScript.
136
171
  * @param {string} source
@@ -199,7 +234,7 @@ exports.lint = lint;
199
234
 
200
235
  /**
201
236
  * The full catalog of diagnostic ids [`lint`] can emit (see
202
- * [`crate::json_api::lint_rules`]).
237
+ * `rsvelte_lint::json_api::lint_rules`).
203
238
  * @returns {string}
204
239
  */
205
240
  function lint_rules() {
@@ -234,6 +269,35 @@ function lint_version() {
234
269
  }
235
270
  exports.lint_version = lint_version;
236
271
 
272
+ /**
273
+ * [`lint`] under the text of a `rsvelte-lint.json`. Wasm has no filesystem, so
274
+ * the host (the language server) discovers the config file and passes its
275
+ * contents in; `""` selects the recommended preset.
276
+ * @param {string} source
277
+ * @param {string} filename
278
+ * @param {string} config
279
+ * @returns {string}
280
+ */
281
+ function lint_with_config(source, filename, config) {
282
+ let deferred4_0;
283
+ let deferred4_1;
284
+ try {
285
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
286
+ const len0 = WASM_VECTOR_LEN;
287
+ const ptr1 = passStringToWasm0(filename, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
288
+ const len1 = WASM_VECTOR_LEN;
289
+ const ptr2 = passStringToWasm0(config, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
290
+ const len2 = WASM_VECTOR_LEN;
291
+ const ret = wasm.lint_with_config(ptr0, len0, ptr1, len1, ptr2, len2);
292
+ deferred4_0 = ret[0];
293
+ deferred4_1 = ret[1];
294
+ return getStringFromWasm0(ret[0], ret[1]);
295
+ } finally {
296
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
297
+ }
298
+ }
299
+ exports.lint_with_config = lint_with_config;
300
+
237
301
  /**
238
302
  * Parse a Svelte component and return the AST as JSON.
239
303
  * @param {string} source
@@ -294,9 +358,41 @@ exports.version = version;
294
358
  function __wbg_get_imports() {
295
359
  const import0 = {
296
360
  __proto__: null,
361
+ __wbg___wbindgen_boolean_get_fa956cfa2d1bd751: function(arg0) {
362
+ const v = arg0;
363
+ const ret = typeof(v) === 'boolean' ? v : undefined;
364
+ return isLikeNone(ret) ? 0xFFFFFF : ret ? 1 : 0;
365
+ },
366
+ __wbg___wbindgen_is_function_1ff95bcc5517c252: function(arg0) {
367
+ const ret = typeof(arg0) === 'function';
368
+ return ret;
369
+ },
370
+ __wbg___wbindgen_is_null_ea9085d691f535d3: function(arg0) {
371
+ const ret = arg0 === null;
372
+ return ret;
373
+ },
374
+ __wbg___wbindgen_is_undefined_c05833b95a3cf397: function(arg0) {
375
+ const ret = arg0 === undefined;
376
+ return ret;
377
+ },
378
+ __wbg___wbindgen_string_get_b0ca35b86a603356: function(arg0, arg1) {
379
+ const obj = arg1;
380
+ const ret = typeof(obj) === 'string' ? obj : undefined;
381
+ var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
382
+ var len1 = WASM_VECTOR_LEN;
383
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
384
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
385
+ },
297
386
  __wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
298
387
  throw new Error(getStringFromWasm0(arg0, arg1));
299
388
  },
389
+ __wbg__wbg_cb_unref_fffb441def202758: function(arg0) {
390
+ arg0._wbg_cb_unref();
391
+ },
392
+ __wbg_call_a6e5c5dce5018821: function() { return handleError(function (arg0, arg1, arg2) {
393
+ const ret = arg0.call(arg1, arg2);
394
+ return ret;
395
+ }, arguments); },
300
396
  __wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
301
397
  let deferred0_0;
302
398
  let deferred0_1;
@@ -308,10 +404,26 @@ function __wbg_get_imports() {
308
404
  wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
309
405
  }
310
406
  },
407
+ __wbg_get_78f252d074a84d0b: function() { return handleError(function (arg0, arg1) {
408
+ const ret = Reflect.get(arg0, arg1);
409
+ return ret;
410
+ }, arguments); },
311
411
  __wbg_new_227d7c05414eb861: function() {
312
412
  const ret = new Error();
313
413
  return ret;
314
414
  },
415
+ __wbg_new_da52cf8fe3429cb2: function() {
416
+ const ret = new Object();
417
+ return ret;
418
+ },
419
+ __wbg_parse_1c0d8a8656d7e016: function() { return handleError(function (arg0, arg1) {
420
+ const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
421
+ return ret;
422
+ }, arguments); },
423
+ __wbg_set_8535240470bf2500: function() { return handleError(function (arg0, arg1, arg2) {
424
+ const ret = Reflect.set(arg0, arg1, arg2);
425
+ return ret;
426
+ }, arguments); },
315
427
  __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
316
428
  const ret = arg1.stack;
317
429
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -319,6 +431,16 @@ function __wbg_get_imports() {
319
431
  getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
320
432
  getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
321
433
  },
434
+ __wbindgen_cast_0000000000000001: function(arg0, arg1) {
435
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [String], shim_idx: 5, ret: String, inner_ret: Some(String) }, mutable: false }) -> Externref`.
436
+ const ret = makeClosure(arg0, arg1, wasm_bindgen_de7100a3341595c3___convert__closures_____invoke___alloc_a941a2cfab7efbc4___string__String__alloc_a941a2cfab7efbc4___string__String__true_);
437
+ return ret;
438
+ },
439
+ __wbindgen_cast_0000000000000002: function(arg0, arg1) {
440
+ // Cast intrinsic for `Ref(String) -> Externref`.
441
+ const ret = getStringFromWasm0(arg0, arg1);
442
+ return ret;
443
+ },
322
444
  __wbindgen_init_externref_table: function() {
323
445
  const table = wasm.__wbindgen_externrefs;
324
446
  const offset = table.grow(4);
@@ -335,6 +457,21 @@ function __wbg_get_imports() {
335
457
  };
336
458
  }
337
459
 
460
+ function wasm_bindgen_de7100a3341595c3___convert__closures_____invoke___alloc_a941a2cfab7efbc4___string__String__alloc_a941a2cfab7efbc4___string__String__true_(arg0, arg1, arg2) {
461
+ let deferred2_0;
462
+ let deferred2_1;
463
+ try {
464
+ const ptr0 = passStringToWasm0(arg2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
465
+ const len0 = WASM_VECTOR_LEN;
466
+ const ret = wasm.wasm_bindgen_de7100a3341595c3___convert__closures_____invoke___alloc_a941a2cfab7efbc4___string__String__alloc_a941a2cfab7efbc4___string__String__true_(arg0, arg1, ptr0, len0);
467
+ deferred2_0 = ret[0];
468
+ deferred2_1 = ret[1];
469
+ return getStringFromWasm0(ret[0], ret[1]);
470
+ } finally {
471
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
472
+ }
473
+ }
474
+
338
475
  const CompileResultWasmFinalization = (typeof FinalizationRegistry === 'undefined')
339
476
  ? { register: () => {}, unregister: () => {} }
340
477
  : new FinalizationRegistry(ptr => wasm.__wbg_compileresultwasm_free(ptr, 1));
@@ -342,6 +479,16 @@ const ParseResultWasmFinalization = (typeof FinalizationRegistry === 'undefined'
342
479
  ? { register: () => {}, unregister: () => {} }
343
480
  : new FinalizationRegistry(ptr => wasm.__wbg_parseresultwasm_free(ptr, 1));
344
481
 
482
+ function addToExternrefTable0(obj) {
483
+ const idx = wasm.__externref_table_alloc();
484
+ wasm.__wbindgen_externrefs.set(idx, obj);
485
+ return idx;
486
+ }
487
+
488
+ const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
489
+ ? { register: () => {}, unregister: () => {} }
490
+ : new FinalizationRegistry(state => wasm.__wbindgen_destroy_closure(state.a, state.b));
491
+
345
492
  let cachedDataViewMemory0 = null;
346
493
  function getDataViewMemory0() {
347
494
  if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
@@ -362,6 +509,44 @@ function getUint8ArrayMemory0() {
362
509
  return cachedUint8ArrayMemory0;
363
510
  }
364
511
 
512
+ function handleError(f, args) {
513
+ try {
514
+ return f.apply(this, args);
515
+ } catch (e) {
516
+ const idx = addToExternrefTable0(e);
517
+ wasm.__wbindgen_exn_store(idx);
518
+ }
519
+ }
520
+
521
+ function isLikeNone(x) {
522
+ return x === undefined || x === null;
523
+ }
524
+
525
+ function makeClosure(arg0, arg1, f) {
526
+ const state = { a: arg0, b: arg1, cnt: 1 };
527
+ const real = (...args) => {
528
+
529
+ // First up with a closure we increment the internal reference
530
+ // count. This ensures that the Rust closure environment won't
531
+ // be deallocated while we're invoking it.
532
+ state.cnt++;
533
+ try {
534
+ return f(state.a, state.b, ...args);
535
+ } finally {
536
+ real._wbg_cb_unref();
537
+ }
538
+ };
539
+ real._wbg_cb_unref = () => {
540
+ if (--state.cnt === 0) {
541
+ wasm.__wbindgen_destroy_closure(state.a, state.b);
542
+ state.a = 0;
543
+ CLOSURE_DTORS.unregister(state);
544
+ }
545
+ };
546
+ CLOSURE_DTORS.register(real, state, state);
547
+ return real;
548
+ }
549
+
365
550
  function passStringToWasm0(arg, malloc, realloc) {
366
551
  if (realloc === undefined) {
367
552
  const buf = cachedTextEncoder.encode(arg);
@@ -399,6 +584,12 @@ function passStringToWasm0(arg, malloc, realloc) {
399
584
  return ptr;
400
585
  }
401
586
 
587
+ function takeFromExternrefTable0(idx) {
588
+ const value = wasm.__wbindgen_externrefs.get(idx);
589
+ wasm.__externref_table_dealloc(idx);
590
+ return value;
591
+ }
592
+
402
593
  let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
403
594
  cachedTextDecoder.decode();
404
595
  function decodeText(ptr, len) {
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsvelte/language-server",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Language Server for rsvelte — formatting (native rsvelte-fmt) and linting (bundled rsvelte_lint wasm)",
5
5
  "license": "MIT",
6
6
  "repository": {