@knip/language-server 3.1.2 → 3.1.3

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
@@ -108,7 +108,8 @@ Optionally, the client can implement:
108
108
  - Show contention: ambiguous or shadowed exports behind re-exports
109
109
  - Show contention: converged (diamond-shaped) re-export paths
110
110
 
111
- Each `File.contention[name].sites` entry describes where contention occurs, its kind, and the originating bindings.
111
+ Each `File.contention[name].sites` entry describes where contention occurs, its
112
+ kind, and the originating bindings.
112
113
 
113
114
  Example:
114
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knip/language-server",
3
- "version": "3.1.2",
3
+ "version": "3.1.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/webpro-nl/knip.git",
@@ -18,7 +18,7 @@
18
18
  "./constants": "./src/constants.js"
19
19
  },
20
20
  "dependencies": {
21
- "knip": "^6.44.0",
21
+ "knip": "^6.44.1",
22
22
  "vscode-languageserver": "^10.1.2",
23
23
  "vscode-languageserver-textdocument": "^1.0.16"
24
24
  },
package/src/server.js CHANGED
@@ -100,6 +100,10 @@ export class LanguageServer {
100
100
  session;
101
101
 
102
102
  revision = 0;
103
+
104
+ /** @type {undefined | number} */
105
+ failedRevision;
106
+
103
107
  /** @type {Rules} */
104
108
  rules = {};
105
109
 
@@ -276,6 +280,7 @@ export class LanguageServer {
276
280
  this.connection.sendNotification(NOTIFICATION_MODULE_GRAPH_BUILT, { duration: Date.now() - start });
277
281
  } catch (_error) {
278
282
  if (revision !== this.revision) return;
283
+ this.failedRevision = revision;
279
284
  this.connection.console.error(`Error: ${_error}`);
280
285
  }
281
286
  }
@@ -308,8 +313,8 @@ export class LanguageServer {
308
313
  async handleFileChanges(params) {
309
314
  this.fileCache = undefined;
310
315
  this.packageJsonCache = undefined;
311
- if (!this.session) return;
312
316
  const session = this.session;
317
+ if (!session && this.failedRevision !== this.revision) return;
313
318
 
314
319
  const cwd = this.cwd ?? process.cwd();
315
320
 
@@ -325,7 +330,7 @@ export class LanguageServer {
325
330
  changes.push({ type, filePath });
326
331
  }
327
332
 
328
- if (changes.length === 0) return;
333
+ if (!session || changes.length === 0) return;
329
334
 
330
335
  try {
331
336
  const result = await session.handleFileChanges(changes);
@@ -27,6 +27,7 @@ const cwd = mkdtempSync(path.join(tmpdir(), 'knip-ls-lifecycle-'));
27
27
  after(() => rmSync(cwd, { recursive: true, force: true }));
28
28
  const filePath = path.join(cwd, 'value.js');
29
29
  const changes = { changes: [{ uri: pathToFileURL(filePath).href, type: 2 }] };
30
+ const changed = file => ({ changes: [{ uri: pathToFileURL(path.join(cwd, file)).href, type: 2 }] });
30
31
  const updated = { duration: 1, mem: 1024 };
31
32
  const config = { editor: { exports: { quickfix: { enabled: true } } } };
32
33
  const options = { rules: { exports: 'warn' } };
@@ -134,6 +135,53 @@ test('overlapping starts retain the latest session and ignore obsolete startup e
134
135
  assert.equal(connection.sendNotification.mock.callCount(), 1);
135
136
  });
136
137
 
138
+ for (const file of ['knip.json', 'package.json']) {
139
+ test(`${file} change restarts after a failed start`, { timeout: 3000 }, async t => {
140
+ const server = setup(t);
141
+ let calls = 0;
142
+ t.mock.method(knip, 'createOptions', async () => {
143
+ if (++calls === 1) throw new Error('invalid config');
144
+ return options;
145
+ });
146
+ await server.start();
147
+ assert.equal(server.getResults(), null);
148
+ assert.equal(connection.console.error.mock.callCount(), 1);
149
+ await server.handleFileChanges(changed(file));
150
+ assert.equal(calls, 2);
151
+ assert.deepEqual(server.getResults(), { name: 'current' });
152
+ assert.equal(connection.sendNotification.mock.callCount(), 1);
153
+ });
154
+ }
155
+
156
+ test('config change does not start a server stopped after a failed start', { timeout: 3000 }, async t => {
157
+ const server = setup(t);
158
+ let calls = 0;
159
+ t.mock.method(knip, 'createOptions', async () => {
160
+ calls++;
161
+ throw new Error('invalid config');
162
+ });
163
+ await server.start();
164
+ server.stop();
165
+ await server.handleFileChanges(changed('knip.json'));
166
+ assert.equal(calls, 1);
167
+ assert.equal(server.getResults(), null);
168
+ assert.equal(connection.sendNotification.mock.callCount(), 0);
169
+ });
170
+
171
+ test('source change after a failed start neither restarts nor updates', { timeout: 3000 }, async t => {
172
+ const server = setup(t);
173
+ let calls = 0;
174
+ t.mock.method(knip, 'createOptions', async () => {
175
+ calls++;
176
+ throw new Error('invalid config');
177
+ });
178
+ await server.start();
179
+ await server.handleFileChanges(changes);
180
+ assert.equal(calls, 1);
181
+ assert.equal(server.getResults(), null);
182
+ assert.equal(connection.console.error.mock.callCount(), 1);
183
+ });
184
+
137
185
  test('graph updates invalidate descriptors requested during the update and notify without diagnostics', async t => {
138
186
  const server = setup(t);
139
187
  let session = createSession('before');