@alint-js/cli 0.0.22 → 0.0.24

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
@@ -421,11 +421,7 @@ import { defineRule } from '@alint-js/core'
421
421
 
422
422
  export const checkFunctionRule = defineRule({
423
423
  create: ctx => ({
424
- async onTarget(target) {
425
- if (target.kind !== 'function') {
426
- return
427
- }
428
-
424
+ async onTargetFunction(target) {
429
425
  const model = await ctx.model({ capabilities: ['tool-call'], size: 'small' })
430
426
 
431
427
  ctx.report({
@@ -467,7 +463,7 @@ Rule authors can opt out of caching when a rule depends on external state:
467
463
  defineRule({
468
464
  cache: false,
469
465
  create: ctx => ({
470
- onTarget(target) {
466
+ onTargetFile(target) {
471
467
  // Always reruns.
472
468
  },
473
469
  }),
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as executeCli } from "../cli-CmLooFry.mjs";
2
+ import { t as executeCli } from "../cli-BsgD2UWS.mjs";
3
3
  import process from "node:process";
4
4
  //#region src/bin/index.ts
5
5
  executeCli(process.argv, {
@@ -1,12 +1,12 @@
1
1
  import process from "node:process";
2
2
  import { inspect } from "node:util";
3
3
  import { cac } from "cac";
4
+ import { appendFile, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
4
5
  import { getGlobalSetupConfigPath, getProjectSetupConfigPath, getStatsDir, installStaticPlugins, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, writeSetupConfig } from "@alint-js/config";
5
- import { AlintRunError, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, resolveConfigForFile, runAlint } from "@alint-js/core";
6
+ import { AlintRunError, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, resolveConfigForDirectory, resolveConfigForFile, runAlint } from "@alint-js/core";
6
7
  import { isAbsolute, join, relative, resolve } from "pathe";
7
8
  import { getBorderCharacters, table } from "table";
8
9
  import { errorMessageFrom } from "@moeru/std/error";
9
- import { appendFile, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
10
10
  import c, { createColors } from "tinyrainbow";
11
11
  import cliSpinners from "cli-spinners";
12
12
  import { relative as relative$1 } from "node:path";
@@ -14,7 +14,7 @@ import Gitignore from "gitignore-fs";
14
14
  import { Minimatch, minimatch } from "minimatch";
15
15
  import { errorMessageFrom as errorMessageFrom$1 } from "@moeru/std";
16
16
  //#region package.json
17
- var version = "0.0.22";
17
+ var version = "0.0.24";
18
18
  //#endregion
19
19
  //#region src/cli/commands/command.ts
20
20
  function defineCommand(node) {
@@ -208,15 +208,17 @@ function shouldSkipOptionValue(arg) {
208
208
  //#endregion
209
209
  //#region src/cli/commands/config/inspect.ts
210
210
  const inspect$2 = defineCommand({
211
- action: (context, file, options) => runConfigInspectCommand(file, options.config, context.io),
212
- arguments: "<file>",
213
- description: "Inspect resolved config for a file",
211
+ action: (context, path, options) => runConfigInspectCommand(path, options.config, context.io),
212
+ arguments: "<path>",
213
+ description: "Inspect resolved config for a file or directory",
214
214
  name: "inspect"
215
215
  });
216
- async function runConfigInspectCommand(file, configPath, io) {
216
+ async function runConfigInspectCommand(path, configPath, io) {
217
217
  const config = await loadAlintConfig(io.cwd, configPath);
218
- const result = resolveConfigForFile(resolve(io.cwd, file), config, { cwd: io.cwd });
219
- io.stdout.write(`file: ${file}\n`);
218
+ const targetPath = resolve(io.cwd, path);
219
+ const isDirectory = await stat(targetPath).then((stats) => stats.isDirectory()).catch(() => false);
220
+ const result = isDirectory ? resolveConfigForDirectory(targetPath, config, { cwd: io.cwd }) : resolveConfigForFile(targetPath, config, { cwd: io.cwd });
221
+ io.stdout.write(`${isDirectory ? "directory" : "file"}: ${path}\n`);
220
222
  io.stdout.write(`ignored: ${result.ignored ? "yes" : "no"}\n`);
221
223
  io.stdout.write("matched:\n");
222
224
  for (const item of result.matched) io.stdout.write(` - ${item.name ?? "<anonymous>"}\n`);
@@ -546,8 +548,9 @@ function formatJson(result) {
546
548
  const colors$2 = createColors({ force: true });
547
549
  function formatStylish(input, options = {}) {
548
550
  const diagnostics = Array.isArray(input) ? input : input.diagnostics;
549
- const totalTokens = Array.isArray(input) ? void 0 : input.usage.totalTokens;
550
- if (diagnostics.length === 0) return "";
551
+ const result = Array.isArray(input) ? void 0 : input;
552
+ const style = createStyle(options.color === true);
553
+ if (diagnostics.length === 0) return result?.execution?.cached ? `${formatSummary(diagnostics, result, style)}\n` : "";
551
554
  const diagnosticsByFile = /* @__PURE__ */ new Map();
552
555
  for (const diagnostic of diagnostics) {
553
556
  const fileDiagnostics = diagnosticsByFile.get(diagnostic.filePath);
@@ -558,7 +561,6 @@ function formatStylish(input, options = {}) {
558
561
  diagnosticsByFile.set(diagnostic.filePath, [diagnostic]);
559
562
  }
560
563
  const lines = [];
561
- const style = createStyle(options.color === true);
562
564
  for (const [filePath, fileDiagnostics] of diagnosticsByFile) {
563
565
  lines.push(style.file(filePath));
564
566
  for (const diagnostic of fileDiagnostics) {
@@ -569,9 +571,12 @@ function formatStylish(input, options = {}) {
569
571
  }
570
572
  lines.push("");
571
573
  }
572
- lines.push("", formatSummary(diagnostics, totalTokens, style));
574
+ lines.push("", formatSummary(diagnostics, result, style));
573
575
  return `${lines.join("\n")}\n`;
574
576
  }
577
+ function countCachedDiagnostics(diagnostics, severity) {
578
+ return diagnostics.filter((diagnostic) => diagnostic.severity === severity && diagnostic.cached === true).length;
579
+ }
575
580
  function countDiagnostics$2(diagnostics, severity) {
576
581
  return diagnostics.filter((diagnostic) => diagnostic.severity === severity).length;
577
582
  }
@@ -593,13 +598,32 @@ function createStyle(color) {
593
598
  warning: colors$2.yellow
594
599
  };
595
600
  }
596
- function formatSummary(diagnostics, totalTokens, style) {
601
+ function formatDiagnosticCount(total, cached, label) {
602
+ return `${total} ${label}${cached > 0 ? ` (${cached} cached)` : ""}`;
603
+ }
604
+ function formatExecutionSummary(execution) {
605
+ if (!execution || execution.cached === 0) return;
606
+ return `${execution.cached}/${execution.planned} cached`;
607
+ }
608
+ function formatSummary(diagnostics, result, style) {
597
609
  const warnCount = countDiagnostics$2(diagnostics, "warn");
598
610
  const errorCount = countDiagnostics$2(diagnostics, "error");
599
- const tokens = totalTokens === void 0 ? void 0 : `${totalTokens.toLocaleString("en-US")} tokens`;
600
- const problemSummary = [style.warning(`${warnCount} warn`), style.error(`${errorCount} error`)].join(" / ");
611
+ const cachedWarnCount = countCachedDiagnostics(diagnostics, "warn");
612
+ const cachedErrorCount = countCachedDiagnostics(diagnostics, "error");
613
+ const tokens = result === void 0 ? void 0 : formatTokenSummary(result);
614
+ const problemSummary = [style.warning(formatDiagnosticCount(warnCount, cachedWarnCount, "warn")), style.error(formatDiagnosticCount(errorCount, cachedErrorCount, "error"))].join(" / ");
601
615
  if (tokens === void 0) return problemSummary;
602
- return `${problemSummary} | ${style.summaryToken(tokens)}`;
616
+ const execution = formatExecutionSummary(result?.execution);
617
+ return [
618
+ problemSummary,
619
+ style.summaryToken(tokens),
620
+ execution
621
+ ].filter((segment) => segment !== void 0).join(" | ");
622
+ }
623
+ function formatTokenSummary(result) {
624
+ const liveTokens = result.usage.totalTokens.toLocaleString("en-US");
625
+ const cachedTokens = result.usage.cached?.totalTokens ?? 0;
626
+ return `${liveTokens} tokens${cachedTokens > 0 ? ` (${cachedTokens.toLocaleString("en-US")} cached)` : ""}`;
603
627
  }
604
628
  function identity(value) {
605
629
  return value;
@@ -640,7 +664,7 @@ function countDiagnostics$1(diagnostics, severity) {
640
664
  //#region src/cli/reporters/progress/summary.ts
641
665
  const colors$1 = createColors({ force: true });
642
666
  function createSummaryProgressReporter(options) {
643
- const now = () => options.clock?.() ?? Date.now();
667
+ const now = Date.now;
644
668
  const state = {
645
669
  cached: 0,
646
670
  completed: 0,
@@ -988,10 +1012,10 @@ var NoFilesFoundError = class extends Error {
988
1012
  this.pattern = pattern;
989
1013
  }
990
1014
  };
991
- async function findFiles(options) {
1015
+ async function findLintTargets(options) {
992
1016
  const { config, cwd, errorOnUnmatchedPattern = true, globInputPaths = true, inputs } = options;
993
1017
  const gitignore = shouldFilterGitignoredFiles(config) ? new Gitignore() : void 0;
994
- const candidates = await resolveInputFiles({
1018
+ const targets = await resolveInputFiles({
995
1019
  config,
996
1020
  cwd,
997
1021
  errorOnUnmatchedPattern,
@@ -999,13 +1023,16 @@ async function findFiles(options) {
999
1023
  globInputPaths,
1000
1024
  inputs: inputs.length > 0 ? inputs : ["."]
1001
1025
  });
1002
- if (!gitignore || candidates.length === 0) return candidates;
1026
+ if (!gitignore || targets.files.length === 0) return targets;
1003
1027
  const lintFiles = [];
1004
- for (const file of candidates) {
1028
+ for (const file of targets.files) {
1005
1029
  if (await gitignore.ignores(resolve(cwd, file))) continue;
1006
1030
  lintFiles.push(file);
1007
1031
  }
1008
- return lintFiles;
1032
+ return {
1033
+ directories: targets.directories,
1034
+ files: lintFiles
1035
+ };
1009
1036
  }
1010
1037
  function collectGlobalIgnorePatterns(config) {
1011
1038
  return normalizeConfig(config).flatMap((item) => isGlobalIgnoreItem(item) ? [...item.ignores] : []);
@@ -1046,6 +1073,7 @@ async function resolveInputFiles(options) {
1046
1073
  const hasFilePatterns = hasDiscoveryFilePatterns(options.config);
1047
1074
  const ignoredPatterns = collectGlobalIgnorePatterns(options.config);
1048
1075
  const candidates = [];
1076
+ const directories = [];
1049
1077
  for (const input of options.inputs) {
1050
1078
  const path = resolve(options.cwd, input);
1051
1079
  const stats = await statPath(path);
@@ -1054,12 +1082,13 @@ async function resolveInputFiles(options) {
1054
1082
  continue;
1055
1083
  }
1056
1084
  if (stats?.isDirectory()) {
1057
- if (!hasFilePatterns) continue;
1058
1085
  if (await shouldPruneDirectory(path, {
1059
1086
  cwd: options.cwd,
1060
1087
  gitignore: options.gitignore,
1061
1088
  ignoredPatterns
1062
1089
  })) continue;
1090
+ directories.push(input);
1091
+ if (!hasFilePatterns) continue;
1063
1092
  const directoryFiles = (await walkFiles(path, {
1064
1093
  cwd: options.cwd,
1065
1094
  gitignore: options.gitignore,
@@ -1086,7 +1115,10 @@ async function resolveInputFiles(options) {
1086
1115
  }
1087
1116
  if (options.errorOnUnmatchedPattern) throw new NoFilesFoundError(input, { globInputPaths: options.globInputPaths });
1088
1117
  }
1089
- return [...new Set(candidates)];
1118
+ return {
1119
+ directories: [...new Set(directories)],
1120
+ files: [...new Set(candidates)]
1121
+ };
1090
1122
  }
1091
1123
  async function searchGlob(options) {
1092
1124
  const root = resolve(options.cwd, getGlobParent(options.pattern));
@@ -1256,11 +1288,10 @@ function parseSince(time, now) {
1256
1288
  const DEFAULT_RETENTION_MONTHS = 12;
1257
1289
  const STATS_FILE_PATTERN = /^stats-(\d{4})-(\d{2})\.jsonl$/u;
1258
1290
  function createJsonlStatsStore(options) {
1259
- const now = options.clock ?? Date.now;
1260
1291
  const retentionMonths = options.retentionMonths ?? DEFAULT_RETENTION_MONTHS;
1261
1292
  return {
1262
- query: (filter) => query(options.dir, filter ?? {}, now),
1263
- record: (input) => record(options.dir, input, now(), retentionMonths)
1293
+ query: (filter) => query(options.dir, filter ?? {}, Date.now),
1294
+ record: (input) => record(options.dir, input, Date.now(), retentionMonths)
1264
1295
  };
1265
1296
  }
1266
1297
  function accumulate(rows, key, inTok, outTok, totalTok, countRun) {
@@ -1539,9 +1570,9 @@ function isNodeError(error) {
1539
1570
  async function runLintCommand(files, options, io, interceptConsoleOutput) {
1540
1571
  if (options.config) await assertConfigExists(io.cwd, options.config);
1541
1572
  const [setupConfig, config] = await Promise.all([loadMergedSetupConfig(io), loadAlintConfig(io.cwd, options.config)]);
1542
- let lintFiles;
1573
+ let lintTargets;
1543
1574
  try {
1544
- lintFiles = await findFiles({
1575
+ lintTargets = await findLintTargets({
1545
1576
  config,
1546
1577
  cwd: io.cwd,
1547
1578
  errorOnUnmatchedPattern: true,
@@ -1571,7 +1602,8 @@ async function runLintCommand(files, options, io, interceptConsoleOutput) {
1571
1602
  result = await runAlint({
1572
1603
  config,
1573
1604
  cwd: io.cwd,
1574
- files: lintFiles,
1605
+ directories: lintTargets.directories,
1606
+ files: lintTargets.files,
1575
1607
  modelOverride: options.model,
1576
1608
  outputLanguage: options.outputLanguage,
1577
1609
  progress: mergeProgressReporters(progress?.reporter, statsCollector?.reporter),
@@ -1591,7 +1623,7 @@ async function runLintCommand(files, options, io, interceptConsoleOutput) {
1591
1623
  progress?.dispose();
1592
1624
  if (statsTarget && statsCollector) await writeRunStats(statsTarget, statsCollector, result, io.cwd);
1593
1625
  io.stdout.write(formatDiagnostics(options.format, result, { color: io.stdout.isTTY === true }));
1594
- return result.diagnostics.length > 0 ? 1 : 0;
1626
+ return result.diagnostics.some((diagnostic) => diagnostic.severity === "error") ? 1 : 0;
1595
1627
  }
1596
1628
  function shouldEnableProgress(options, io) {
1597
1629
  if (options.progress !== void 0) return options.progress;
@@ -1646,7 +1678,7 @@ async function inspectOutputFile(io, file, options) {
1646
1678
  io.stderr.write(`${errorMessageFrom$1(error)}\n`);
1647
1679
  return 2;
1648
1680
  }
1649
- return parsed.diagnostics.length > 0 ? 1 : 0;
1681
+ return parsed.diagnostics.some((diagnostic) => diagnostic.severity === "error") ? 1 : 0;
1650
1682
  }
1651
1683
  function isDiagnostic(value) {
1652
1684
  if (!isRecord(value)) return false;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-CmLooFry.mjs";
1
+ import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-BsgD2UWS.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 };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alint-js/cli",
3
3
  "type": "module",
4
- "version": "0.0.22",
4
+ "version": "0.0.24",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.mts",
@@ -25,8 +25,8 @@
25
25
  "pathe": "^2.0.3",
26
26
  "table": "^6.9.0",
27
27
  "tinyrainbow": "^3.1.0",
28
- "@alint-js/config": "0.0.22",
29
- "@alint-js/core": "0.0.22"
28
+ "@alint-js/config": "0.0.24",
29
+ "@alint-js/core": "0.0.24"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@pnpm/find-workspace-dir": "^1000.1.5",