@alint-js/cli 0.5.0 → 0.7.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 +110 -41
- package/dist/bin/index.mjs +1 -1
- package/dist/{cli-ChymU1Wr.mjs → cli-BsBgbtds.mjs} +1937 -1311
- package/dist/index.mjs +1 -1
- package/dist/lsp-commands.d.mts +16 -0
- package/dist/lsp-commands.mjs +16 -0
- package/dist/{server-DgJa6R5l.mjs → server-C-kuoGl1.mjs} +104 -18
- package/package.json +27 -6
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-BsBgbtds.mjs";
|
|
2
2
|
import { ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated } from "@alint-js/config";
|
|
3
3
|
import { defineConfig } from "@alint-js/core";
|
|
4
4
|
export { defineConfig, executeCli, formatDiagnostics, formatJson, formatStylish, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/cli/commands/lsp/command-ids.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Command ids the language server declares in `executeCommandProvider`.
|
|
4
|
+
*
|
|
5
|
+
* They are part of the protocol surface, so any editor integration needs them, not only the VS
|
|
6
|
+
* Code extension in this repository. This module imports nothing, so a client can bundle it
|
|
7
|
+
* without pulling in the CLI.
|
|
8
|
+
*/
|
|
9
|
+
/** Deletes cached diagnostics. A client asks the user before it sends this. */
|
|
10
|
+
declare const CLEAR_CACHE_COMMAND = "alint.clearCache";
|
|
11
|
+
/** Runs one file. The argument is the document URI. */
|
|
12
|
+
declare const RUN_FILE_COMMAND = "alint.runFile";
|
|
13
|
+
/** Runs every file the config matches. It takes no arguments. */
|
|
14
|
+
declare const RUN_WORKSPACE_COMMAND = "alint.runWorkspace";
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CLEAR_CACHE_COMMAND, RUN_FILE_COMMAND, RUN_WORKSPACE_COMMAND };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/cli/commands/lsp/command-ids.ts
|
|
2
|
+
/**
|
|
3
|
+
* Command ids the language server declares in `executeCommandProvider`.
|
|
4
|
+
*
|
|
5
|
+
* They are part of the protocol surface, so any editor integration needs them, not only the VS
|
|
6
|
+
* Code extension in this repository. This module imports nothing, so a client can bundle it
|
|
7
|
+
* without pulling in the CLI.
|
|
8
|
+
*/
|
|
9
|
+
/** Deletes cached diagnostics. A client asks the user before it sends this. */
|
|
10
|
+
const CLEAR_CACHE_COMMAND = "alint.clearCache";
|
|
11
|
+
/** Runs one file. The argument is the document URI. */
|
|
12
|
+
const RUN_FILE_COMMAND = "alint.runFile";
|
|
13
|
+
/** Runs every file the config matches. It takes no arguments. */
|
|
14
|
+
const RUN_WORKSPACE_COMMAND = "alint.runWorkspace";
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CLEAR_CACHE_COMMAND, RUN_FILE_COMMAND, RUN_WORKSPACE_COMMAND };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as createRunSession } from "./cli-BsBgbtds.mjs";
|
|
2
|
+
import { CLEAR_CACHE_COMMAND, RUN_FILE_COMMAND, RUN_WORKSPACE_COMMAND } from "./lsp-commands.mjs";
|
|
3
|
+
import { realpath, rm } from "node:fs/promises";
|
|
3
4
|
import { AlintRunCancelledError, AlintRunError } from "@alint-js/core";
|
|
4
|
-
import { join, relative, sep } from "node:path";
|
|
5
5
|
import { errorMessageFrom } from "@moeru/std";
|
|
6
|
+
import { join, relative, sep } from "node:path";
|
|
6
7
|
import { Writable } from "node:stream";
|
|
7
8
|
import { DidChangeWatchedFilesNotification } from "vscode-languageserver";
|
|
8
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -90,12 +91,51 @@ async function createFolderSession(options) {
|
|
|
90
91
|
let session = await createRunSession(runIo);
|
|
91
92
|
let diagnostics = /* @__PURE__ */ new Map();
|
|
92
93
|
let scheduled;
|
|
94
|
+
let explicitRun = Promise.resolve();
|
|
95
|
+
let explicitRunning = false;
|
|
93
96
|
const replaceMap = (next) => {
|
|
94
97
|
const changed = /* @__PURE__ */ new Set([...next.keys(), ...diagnostics.keys()]);
|
|
95
98
|
diagnostics = next;
|
|
96
99
|
return [...changed];
|
|
97
100
|
};
|
|
101
|
+
const runOnce = async (runOptions) => {
|
|
102
|
+
const runPaths = runOptions.inputs?.map((uri) => toRunPath(uri, paths)).filter((path) => path !== void 0);
|
|
103
|
+
const changed = /* @__PURE__ */ new Set();
|
|
104
|
+
const merge = (result) => {
|
|
105
|
+
for (const [uri, list] of groupByUri(result, paths)) {
|
|
106
|
+
diagnostics.set(uri, list);
|
|
107
|
+
changed.add(uri);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
explicitRunning = true;
|
|
111
|
+
try {
|
|
112
|
+
merge(await session.run({
|
|
113
|
+
inputs: runPaths,
|
|
114
|
+
progress: {
|
|
115
|
+
onDiagnostic: ({ diagnostic }) => {
|
|
116
|
+
const uri = toDocumentUri(diagnostic.filePath, paths);
|
|
117
|
+
diagnostics.set(uri, [...diagnostics.get(uri) ?? [], toLspDiagnostic(diagnostic)]);
|
|
118
|
+
changed.add(uri);
|
|
119
|
+
},
|
|
120
|
+
onJobStart: ({ job, progress }) => runOptions.onProgress?.(progress, job.inputPath)
|
|
121
|
+
},
|
|
122
|
+
projectTargets: runPaths === void 0 ? void 0 : false,
|
|
123
|
+
runner: session.runner,
|
|
124
|
+
signal: runOptions.signal
|
|
125
|
+
}));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (!(error instanceof AlintRunError) && !(error instanceof AlintRunCancelledError)) throw error;
|
|
128
|
+
merge(error.result);
|
|
129
|
+
} finally {
|
|
130
|
+
explicitRunning = false;
|
|
131
|
+
if (changed.size > 0) options.onChanged?.([...changed]);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
98
134
|
return {
|
|
135
|
+
clearCache: async () => {
|
|
136
|
+
await rm(session.cache.location, { force: true });
|
|
137
|
+
return replaceMap(/* @__PURE__ */ new Map());
|
|
138
|
+
},
|
|
99
139
|
cwd,
|
|
100
140
|
get diagnostics() {
|
|
101
141
|
return diagnostics;
|
|
@@ -110,8 +150,8 @@ async function createFolderSession(options) {
|
|
|
110
150
|
folderUri: options.folderUri,
|
|
111
151
|
refreshFile: async (uri) => {
|
|
112
152
|
const filePath = toRunPath(uri, paths);
|
|
113
|
-
if (filePath === void 0) return [];
|
|
114
|
-
const found = groupByUri(await runFromCache(session, {
|
|
153
|
+
if (filePath === void 0 || explicitRunning) return [];
|
|
154
|
+
const found = groupByUri(await runFromCache(session, { inputs: [filePath] }), paths);
|
|
115
155
|
const changed = /* @__PURE__ */ new Set([...found.keys()]);
|
|
116
156
|
const documentUri = toDocumentUri(filePath, paths);
|
|
117
157
|
if (!found.has(documentUri) && diagnostics.delete(documentUri)) changed.add(documentUri);
|
|
@@ -119,6 +159,7 @@ async function createFolderSession(options) {
|
|
|
119
159
|
return [...changed];
|
|
120
160
|
},
|
|
121
161
|
refreshFromCache: async () => {
|
|
162
|
+
if (explicitRunning) return;
|
|
122
163
|
replaceMap(groupByUri(await runFromCache(session), paths));
|
|
123
164
|
},
|
|
124
165
|
reloadConfig: async () => {
|
|
@@ -126,12 +167,17 @@ async function createFolderSession(options) {
|
|
|
126
167
|
session = await createRunSession(runIo);
|
|
127
168
|
await replaced.shutdown();
|
|
128
169
|
},
|
|
170
|
+
runExplicit: (runOptions) => {
|
|
171
|
+
explicitRun = explicitRun.catch(() => {}).then(() => runOnce(runOptions));
|
|
172
|
+
return explicitRun;
|
|
173
|
+
},
|
|
129
174
|
scheduleWorkspacePass: () => {
|
|
130
175
|
if (scheduled !== void 0) return;
|
|
131
176
|
scheduled = setTimeout(() => {
|
|
132
177
|
scheduled = void 0;
|
|
133
178
|
(async () => {
|
|
134
179
|
try {
|
|
180
|
+
if (explicitRunning) return;
|
|
135
181
|
options.onChanged?.(replaceMap(groupByUri(await runFromCache(session), paths)));
|
|
136
182
|
} catch (error) {
|
|
137
183
|
options.io.stderr.write(`alint lsp: workspace pass failed: ${errorMessageFrom(error) ?? "unknown error"}\n`);
|
|
@@ -159,8 +205,8 @@ async function runFromCache(session, options = {}) {
|
|
|
159
205
|
try {
|
|
160
206
|
return await session.run({
|
|
161
207
|
cacheOnly: true,
|
|
162
|
-
|
|
163
|
-
projectTargets: options.
|
|
208
|
+
inputs: options.inputs,
|
|
209
|
+
projectTargets: options.inputs === void 0 ? void 0 : false,
|
|
164
210
|
runner: {
|
|
165
211
|
...session.runner,
|
|
166
212
|
stats: false
|
|
@@ -197,9 +243,9 @@ function toRunPath(uri, paths) {
|
|
|
197
243
|
/**
|
|
198
244
|
* Collects document URIs and publishes each one once per window, open documents first.
|
|
199
245
|
*
|
|
200
|
-
* An LSP publish
|
|
201
|
-
* many times. A workspace pass can queue thousands of documents, and the open one must
|
|
202
|
-
* for the rest.
|
|
246
|
+
* An LSP publish contains a whole document, so one publish per diagnostic would send the same
|
|
247
|
+
* document many times. A workspace pass can queue thousands of documents, and the open one must
|
|
248
|
+
* not wait for the rest.
|
|
203
249
|
*/
|
|
204
250
|
function createPublisher(options) {
|
|
205
251
|
const open = /* @__PURE__ */ new Set();
|
|
@@ -238,9 +284,6 @@ function createPublisher(options) {
|
|
|
238
284
|
//#region src/cli/commands/lsp/server.ts
|
|
239
285
|
/** Long enough to collect a workspace pass into few messages, short enough to keep a save prompt. */
|
|
240
286
|
const PUBLISH_FLUSH_MS = 200;
|
|
241
|
-
/** Two commands, because a workspace run can cost orders of magnitude more than one file. */
|
|
242
|
-
const RUN_FILE_COMMAND = "alint.runFile";
|
|
243
|
-
const RUN_WORKSPACE_COMMAND = "alint.runWorkspace";
|
|
244
287
|
async function startLspServer(io) {
|
|
245
288
|
const { stdin } = io;
|
|
246
289
|
if (!stdin) {
|
|
@@ -282,7 +325,11 @@ async function startLspServer(io) {
|
|
|
282
325
|
watchesConfig = params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true;
|
|
283
326
|
return { capabilities: {
|
|
284
327
|
executeCommandProvider: {
|
|
285
|
-
commands: [
|
|
328
|
+
commands: [
|
|
329
|
+
CLEAR_CACHE_COMMAND,
|
|
330
|
+
RUN_FILE_COMMAND,
|
|
331
|
+
RUN_WORKSPACE_COMMAND
|
|
332
|
+
],
|
|
286
333
|
workDoneProgress: true
|
|
287
334
|
},
|
|
288
335
|
textDocumentSync: {
|
|
@@ -302,7 +349,7 @@ async function startLspServer(io) {
|
|
|
302
349
|
try {
|
|
303
350
|
await openFolder(folderUri);
|
|
304
351
|
} catch (error) {
|
|
305
|
-
connection.console.error(
|
|
352
|
+
connection.console.error(`${folderUri}: ${errorMessageFrom(error) ?? "could not read cached diagnostics"}`);
|
|
306
353
|
}
|
|
307
354
|
}));
|
|
308
355
|
});
|
|
@@ -311,7 +358,7 @@ async function startLspServer(io) {
|
|
|
311
358
|
try {
|
|
312
359
|
for (const uri of await folder.refreshFile(textDocument.uri)) publisher.queue(uri);
|
|
313
360
|
} catch (error) {
|
|
314
|
-
connection.console.error(
|
|
361
|
+
connection.console.error(`${textDocument.uri}: ${errorMessageFrom(error) ?? "could not refresh the saved file"}`);
|
|
315
362
|
}
|
|
316
363
|
}));
|
|
317
364
|
});
|
|
@@ -322,16 +369,55 @@ async function startLspServer(io) {
|
|
|
322
369
|
connection.onDidCloseTextDocument(({ textDocument }) => {
|
|
323
370
|
publisher.setOpen(textDocument.uri, false);
|
|
324
371
|
});
|
|
372
|
+
connection.onExecuteCommand(async ({ arguments: args, command }) => {
|
|
373
|
+
if (command === "alint.clearCache") {
|
|
374
|
+
await Promise.all([...folders.values()].map(async (folder) => {
|
|
375
|
+
try {
|
|
376
|
+
for (const uri of await folder.clearCache()) publisher.queue(uri);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
connection.console.error(`${folder.folderUri}: ${errorMessageFrom(error) ?? "could not clear the cache"}`);
|
|
379
|
+
}
|
|
380
|
+
}));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const inputs = command === "alint.runFile" ? args?.filter((value) => typeof value === "string") : void 0;
|
|
384
|
+
if (command === "alint.runFile" && (inputs === void 0 || inputs.length === 0)) {
|
|
385
|
+
connection.console.error(`${RUN_FILE_COMMAND} needs a document URI.`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const reporter = await connection.window.createWorkDoneProgress();
|
|
389
|
+
const controller = new AbortController();
|
|
390
|
+
reporter.begin(inputs === void 0 ? "Running alint on the workspace" : "Running alint on the current file", 0, void 0, true);
|
|
391
|
+
reporter.token.onCancellationRequested(() => controller.abort());
|
|
392
|
+
try {
|
|
393
|
+
await Promise.all([...folders.values()].map(async (folder) => {
|
|
394
|
+
try {
|
|
395
|
+
await folder.runExplicit({
|
|
396
|
+
inputs,
|
|
397
|
+
onProgress: (progress, inputPath) => {
|
|
398
|
+
const percentage = progress.jobsTotal === 0 ? 0 : Math.round(progress.jobsCompleted / progress.jobsTotal * 100);
|
|
399
|
+
reporter.report(percentage, inputPath);
|
|
400
|
+
},
|
|
401
|
+
signal: controller.signal
|
|
402
|
+
});
|
|
403
|
+
} catch (error) {
|
|
404
|
+
connection.console.error(`${folder.folderUri}: ${errorMessageFrom(error) ?? "the run failed"}`);
|
|
405
|
+
}
|
|
406
|
+
}));
|
|
407
|
+
} finally {
|
|
408
|
+
reporter.done();
|
|
409
|
+
}
|
|
410
|
+
});
|
|
325
411
|
connection.onDidChangeWatchedFiles(() => {
|
|
326
412
|
Promise.all([...folders.values()].map(async (folder) => {
|
|
327
413
|
try {
|
|
328
414
|
await folder.reloadConfig();
|
|
329
415
|
folder.scheduleWorkspacePass();
|
|
330
416
|
} catch (error) {
|
|
331
|
-
connection.console.error(
|
|
417
|
+
connection.console.error(`${folder.folderUri}: ${errorMessageFrom(error) ?? "could not reload the config"}`);
|
|
332
418
|
}
|
|
333
419
|
}));
|
|
334
|
-
connection.console.info("
|
|
420
|
+
connection.console.info("Configuration changed. Cached diagnostics were cleared. Run alint again to recreate them.");
|
|
335
421
|
});
|
|
336
422
|
connection.onShutdown(async () => {
|
|
337
423
|
publisher.dispose();
|
package/package.json
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"description": "Agentic code analysis CLI for model-backed lint rules",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/moeru-ai/alint.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"alint",
|
|
14
|
+
"lint",
|
|
15
|
+
"linter",
|
|
16
|
+
"ai",
|
|
17
|
+
"code-analysis",
|
|
18
|
+
"cli"
|
|
19
|
+
],
|
|
5
20
|
"exports": {
|
|
6
21
|
".": {
|
|
7
22
|
"types": "./dist/index.d.mts",
|
|
8
23
|
"default": "./dist/index.mjs"
|
|
9
24
|
},
|
|
25
|
+
"./lsp-commands": {
|
|
26
|
+
"types": "./dist/lsp-commands.d.mts",
|
|
27
|
+
"default": "./dist/lsp-commands.mjs"
|
|
28
|
+
},
|
|
10
29
|
"./package.json": "./package.json"
|
|
11
30
|
},
|
|
12
31
|
"bin": {
|
|
@@ -21,23 +40,25 @@
|
|
|
21
40
|
"cac": "^7.0.0",
|
|
22
41
|
"cli-spinners": "^3.4.0",
|
|
23
42
|
"date-fns": "^4.4.0",
|
|
43
|
+
"diff": "^9.0.0",
|
|
24
44
|
"fast-string-truncated-width": "^3.0.3",
|
|
25
45
|
"fast-string-width": "^3.0.2",
|
|
26
46
|
"gitignore-fs": "^2.2.3",
|
|
27
47
|
"minimatch": "^10.2.6",
|
|
28
48
|
"pathe": "^2.0.3",
|
|
29
49
|
"table": "^6.9.0",
|
|
50
|
+
"tinyexec": "^1.2.4",
|
|
30
51
|
"tinyrainbow": "^3.1.1",
|
|
31
52
|
"vscode-languageserver": "^10.1.0",
|
|
32
|
-
"@alint-js/
|
|
33
|
-
"@alint-js/core": "0.
|
|
34
|
-
"@alint-js/
|
|
53
|
+
"@alint-js/model-adapter-acp": "0.7.0",
|
|
54
|
+
"@alint-js/core": "0.7.0",
|
|
55
|
+
"@alint-js/config": "0.7.0",
|
|
56
|
+
"@alint-js/utils": "0.7.0"
|
|
35
57
|
},
|
|
36
58
|
"devDependencies": {
|
|
37
59
|
"@pnpm/find-workspace-dir": "^1000.1.5",
|
|
38
60
|
"local-pkg": "^1.2.1",
|
|
39
|
-
"mlly": "^1.8.2"
|
|
40
|
-
"tinyexec": "^1.2.4"
|
|
61
|
+
"mlly": "^1.8.2"
|
|
41
62
|
},
|
|
42
63
|
"scripts": {
|
|
43
64
|
"alint": "node ./dist/bin/index.mjs",
|