@alint-js/core 0.0.21 → 0.0.23

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/dist/index.mjs CHANGED
@@ -24,41 +24,14 @@ function matchesDiscoveryFile(filePath, input, options) {
24
24
  function normalizeConfig(input) {
25
25
  return normalizeConfigItems(input, []);
26
26
  }
27
+ function resolveConfigForDirectory(directoryPath, input, options) {
28
+ return resolveConfig(directoryPath, input, options, "directory");
29
+ }
27
30
  function resolveConfigForFile(filePath, input, options) {
28
- const items = expandConfig(input);
29
- const matched = [];
30
- const skipped = [];
31
- const config = createEmptyConfig();
32
- for (const item of items) {
33
- if (isGlobalIgnoreItem(item)) {
34
- if (matchesInheritedScopes(filePath, item, options.cwd) && matchesAny(filePath, item.ignores ?? [], options.cwd, getEffectiveBasePath(item))) return {
35
- config: createEmptyConfig(),
36
- ignored: true,
37
- matched: [],
38
- skipped
39
- };
40
- skipped.push({
41
- item,
42
- reason: "global ignores did not match"
43
- });
44
- continue;
45
- }
46
- if (!matchesConfigItem(filePath, item, options.cwd)) {
47
- skipped.push({
48
- item,
49
- reason: "files or ignores did not match"
50
- });
51
- continue;
52
- }
53
- matched.push(item);
54
- mergeConfig(config, item);
55
- }
56
- return {
57
- config,
58
- ignored: false,
59
- matched,
60
- skipped
61
- };
31
+ return resolveConfig(filePath, input, options, "file");
32
+ }
33
+ function resolveConfigForProject(projectPath, input, options) {
34
+ return resolveConfig(projectPath, input, options, "project");
62
35
  }
63
36
  function createEmptyConfig() {
64
37
  return {
@@ -70,9 +43,10 @@ function createEmptyConfig() {
70
43
  };
71
44
  }
72
45
  function createMatcherScope(item, basePath) {
73
- if (!item.files && !item.ignores) return;
46
+ if (!item.directories && !item.files && !item.ignores) return;
74
47
  return {
75
48
  basePath,
49
+ directories: item.directories,
76
50
  files: item.files,
77
51
  ignores: item.ignores
78
52
  };
@@ -131,6 +105,10 @@ function getInheritedMatcherScopes(item) {
131
105
  function hasDiscoveryMatcher(item) {
132
106
  return hasPositiveFilePattern(item.files) || getInheritedMatcherScopes(item).some((scope) => hasPositiveFilePattern(scope.files));
133
107
  }
108
+ function hasOtherTargetPatterns(item, targetKind) {
109
+ if (targetKind === "project") return item.files !== void 0 || item.directories !== void 0;
110
+ return targetKind === "directory" ? item.files !== void 0 : item.directories !== void 0;
111
+ }
134
112
  function hasPositiveFilePattern(files) {
135
113
  return files?.some((pattern) => isPatternList(pattern) ? pattern.some(isPositivePattern) : isPositivePattern(pattern)) ?? false;
136
114
  }
@@ -147,28 +125,30 @@ function isPatternList(pattern) {
147
125
  function isPositivePattern(pattern) {
148
126
  return !pattern.startsWith("!");
149
127
  }
150
- function matchesAny(filePath, patterns, cwd, basePath) {
151
- return patterns.some((pattern) => matchesPattern(filePath, pattern, cwd, basePath));
152
- }
153
- function matchesConfigItem(filePath, item, cwd) {
128
+ function matchesConfigItem(filePath, item, cwd, targetKind) {
154
129
  const basePath = getEffectiveBasePath(item);
155
- if (!matchesInheritedScopes(filePath, item, cwd)) return false;
156
- if (matchesAny(filePath, item.ignores ?? [], cwd, basePath)) return false;
157
- if (!item.files || item.files.length === 0) return true;
158
- return item.files.some((pattern) => matchesPattern(filePath, pattern, cwd, basePath));
130
+ if (!matchesInheritedScopes(filePath, item, cwd, targetKind)) return false;
131
+ if (matchesIgnores(filePath, item.ignores ?? [], cwd, basePath, targetKind)) return false;
132
+ const patterns = targetPatterns(item, targetKind);
133
+ if (patterns !== void 0) return patterns.length === 0 || patterns.some((pattern) => matchesPattern(filePath, pattern, cwd, basePath));
134
+ return !hasOtherTargetPatterns(item, targetKind);
135
+ }
136
+ function matchesIgnores(targetPath, patterns, cwd, basePath, targetKind) {
137
+ return patterns.some((pattern) => matchesPattern(targetPath, pattern, cwd, basePath, targetKind === "project"));
159
138
  }
160
- function matchesInheritedScopes(filePath, item, cwd) {
139
+ function matchesInheritedScopes(filePath, item, cwd, targetKind) {
161
140
  return getInheritedMatcherScopes(item).every((scope) => {
162
- if (matchesAny(filePath, scope.ignores ?? [], cwd, scope.basePath)) return false;
163
- if (!scope.files || scope.files.length === 0) return true;
164
- return scope.files.some((pattern) => matchesPattern(filePath, pattern, cwd, scope.basePath));
141
+ if (matchesIgnores(filePath, scope.ignores ?? [], cwd, scope.basePath, targetKind)) return false;
142
+ const patterns = targetPatterns(scope, targetKind);
143
+ if (patterns !== void 0) return patterns.length === 0 || patterns.some((pattern) => matchesPattern(filePath, pattern, cwd, scope.basePath));
144
+ return !hasOtherTargetPatterns(scope, targetKind);
165
145
  });
166
146
  }
167
- function matchesPattern(filePath, pattern, cwd, basePath) {
168
- if (isPatternList(pattern)) return pattern.every((entry) => matchesPattern(filePath, entry, cwd, basePath));
147
+ function matchesPattern(filePath, pattern, cwd, basePath, matchDirectoryRoot = false) {
148
+ if (isPatternList(pattern)) return pattern.every((entry) => matchesPattern(filePath, entry, cwd, basePath, matchDirectoryRoot));
169
149
  const relativePath = relative(basePath ? resolve(cwd, basePath) : cwd, isAbsolute(filePath) ? filePath : resolve(cwd, filePath)).replaceAll("\\", "/");
170
150
  if (relativePath === ".." || relativePath.startsWith("../")) return false;
171
- return minimatch(relativePath, pattern, { dot: true });
151
+ return minimatch(relativePath, pattern, { dot: true }) || matchDirectoryRoot && minimatch(`${relativePath}/`, pattern, { dot: true });
172
152
  }
173
153
  function mergeConfig(config, item) {
174
154
  if (item.plugins) for (const [alias, plugin] of Object.entries(item.plugins)) {
@@ -192,6 +172,42 @@ function normalizeConfigItems(input, arrayStack) {
192
172
  if (arrayStack.includes(input)) throw new Error("Circular config array.");
193
173
  return input.flatMap((item) => isConfigArrayInput(item) ? normalizeConfigItems(item, [...arrayStack, input]) : [item]);
194
174
  }
175
+ function resolveConfig(targetPath, input, options, targetKind) {
176
+ const items = expandConfig(input);
177
+ const matched = [];
178
+ const skipped = [];
179
+ const config = createEmptyConfig();
180
+ for (const item of items) {
181
+ if (isGlobalIgnoreItem(item)) {
182
+ if (matchesInheritedScopes(targetPath, item, options.cwd, targetKind) && matchesIgnores(targetPath, item.ignores ?? [], options.cwd, getEffectiveBasePath(item), targetKind)) return {
183
+ config: createEmptyConfig(),
184
+ ignored: true,
185
+ matched: [],
186
+ skipped
187
+ };
188
+ skipped.push({
189
+ item,
190
+ reason: "global ignores did not match"
191
+ });
192
+ continue;
193
+ }
194
+ if (!matchesConfigItem(targetPath, item, options.cwd, targetKind)) {
195
+ skipped.push({
196
+ item,
197
+ reason: "target patterns or ignores did not match"
198
+ });
199
+ continue;
200
+ }
201
+ matched.push(item);
202
+ mergeConfig(config, item);
203
+ }
204
+ return {
205
+ config,
206
+ ignored: false,
207
+ matched,
208
+ skipped
209
+ };
210
+ }
195
211
  function resolvePluginConfig(reference, state) {
196
212
  if (state.stringStack.includes(reference)) throw new Error(`Circular config extends: ${[...state.stringStack, reference].join(" -> ")}`);
197
213
  const separator = reference.indexOf("/");
@@ -204,6 +220,10 @@ function resolvePluginConfig(reference, state) {
204
220
  stringStack: [...state.stringStack, reference]
205
221
  });
206
222
  }
223
+ function targetPatterns(item, targetKind) {
224
+ if (targetKind === "directory") return item.directories;
225
+ if (targetKind === "file") return item.files;
226
+ }
207
227
  function withExpansionMetadata(item, inheritedMatcherScopes, effectiveBasePath) {
208
228
  Object.defineProperties(item, {
209
229
  [effectiveBasePathSymbol]: { value: effectiveBasePath },
@@ -652,9 +672,6 @@ function createBuiltInLanguageRegistry() {
652
672
  return registry;
653
673
  }
654
674
  //#endregion
655
- //#region package.json
656
- var version = "0.0.21";
657
- //#endregion
658
675
  //#region src/dsl/registry.ts
659
676
  function buildRuleRegistry(config) {
660
677
  const rules = /* @__PURE__ */ new Map();
@@ -694,7 +711,7 @@ function resolveModel(registry, options = {}) {
694
711
  const ruleId = options.ruleId ?? "<unknown>";
695
712
  if (options.request !== void 0) {
696
713
  const request = options.request;
697
- const candidate = candidates.find(({ model }) => matchesRequest(model, request));
714
+ const candidate = candidates.find((candidate) => matchesRequest(candidate, request));
698
715
  if (candidate === void 0) throw new Error(`Unknown model "${request}".`);
699
716
  if (!satisfiesHardRequirements(candidate.model, options.requirement)) throw new Error(`Model "${request}" does not satisfy requirement for rule "${ruleId}".`);
700
717
  return toResolvedModel(candidate, options.requirement);
@@ -709,8 +726,12 @@ function flattenModels(registry) {
709
726
  provider
710
727
  })));
711
728
  }
712
- function matchesRequest(model, request) {
713
- return model.id === request || model.name === request || (model.aliases ?? []).includes(request);
729
+ function matchesRequest(candidate, request) {
730
+ return [
731
+ candidate.model.id,
732
+ candidate.model.name,
733
+ ...candidate.model.aliases ?? []
734
+ ].filter((name) => name !== void 0).some((name) => name === request || `${candidate.provider.id}/${name}` === request);
714
735
  }
715
736
  function preferSize(candidates, requirement) {
716
737
  if (requirement?.size === void 0) return candidates[0];
@@ -912,7 +933,43 @@ function statSyncIsDirectory(path) {
912
933
  return statSync(path).isDirectory();
913
934
  }
914
935
  //#endregion
915
- //#region src/core/run.ts
936
+ //#region src/core/targets/directory.ts
937
+ function createDirectoryExecutionPlans(directories, fileOffset) {
938
+ return directories.map((directory, directoryOffset) => {
939
+ const executions = directory.ruleRuntimes.map((runtime) => {
940
+ if (runtime.handlers.onTargetWith) return {
941
+ run: () => runtime.handlers.onTargetWith?.(directory.target),
942
+ runtime
943
+ };
944
+ if (runtime.handlers.onTargetDirectory) return {
945
+ run: () => runtime.handlers.onTargetDirectory?.(directory.target),
946
+ runtime
947
+ };
948
+ }).filter((execution) => execution !== void 0);
949
+ const targets = executions.length === 0 ? [] : [{
950
+ activeFilePath: directory.target.path,
951
+ cacheFilePaths: [],
952
+ configHash: directory.configHash,
953
+ executions,
954
+ identity: directory.target.path,
955
+ kind: "directory",
956
+ language: "directory",
957
+ text: directory.target.path
958
+ }];
959
+ return {
960
+ emitFileProgress: true,
961
+ fileIndex: fileOffset + directoryOffset + 1,
962
+ path: directory.target.path,
963
+ planned: executions.length,
964
+ targets
965
+ };
966
+ });
967
+ }
968
+ //#endregion
969
+ //#region package.json
970
+ var version = "0.0.23";
971
+ //#endregion
972
+ //#region src/core/targets/execution.ts
916
973
  var AlintRuleExecutionError = class extends Error {
917
974
  failure;
918
975
  constructor(error, path) {
@@ -930,177 +987,16 @@ var AlintRuleExecutionError = class extends Error {
930
987
  };
931
988
  }
932
989
  };
933
- var AlintRunError = class extends Error {
934
- failure;
935
- result;
936
- constructor(message, result, options = {}) {
937
- super(message, { cause: options.cause });
938
- this.name = "AlintRunError";
939
- this.failure = options.failure;
940
- this.result = result;
941
- }
942
- };
943
- async function runAlint(options = {}) {
944
- const cwd$1 = options.cwd ?? cwd();
945
- const config = options.config ?? [];
946
- const setupConfig = options.setupConfig ?? {
947
- providers: [],
948
- version: 1
949
- };
950
- const clock = options.runner?.clock ?? Date.now;
951
- const diagnostics = [];
952
- const usage = createUsageAccumulator();
953
- const src = createSourceRuntime();
954
- const normalizedCacheConfig = normalizeRunnerCacheConfig(options.runner?.cache, cwd$1);
955
- const cacheStore = await createCacheStore({
956
- cwd: cwd$1,
957
- enabled: normalizedCacheConfig.enabled,
958
- location: normalizedCacheConfig.location
959
- });
960
- const cacheContext = {
961
- cwd: cwd$1,
962
- enabled: normalizedCacheConfig.enabled,
963
- fileEntryKeys: /* @__PURE__ */ new Map(),
964
- modelHash: stableHash({
965
- modelOverride: options.modelOverride,
966
- outputLanguage: options.outputLanguage,
967
- setupConfig
968
- }),
969
- store: cacheStore
970
- };
971
- const files = (await Promise.all((options.files ?? []).map(async (filePath) => {
972
- const file = await src.readFile(resolve(cwd$1, filePath));
973
- const resolvedConfig = resolveConfigForFile(file.path, config, { cwd: cwd$1 });
974
- if (resolvedConfig.ignored) return;
975
- const effectiveConfig = resolvedConfig.config;
976
- const languageRegistry = createBuiltInLanguageRegistry();
977
- for (const plugin of Object.values(effectiveConfig.plugins)) for (const language of Object.values(plugin.languages ?? {})) registerLanguage(languageRegistry, language);
978
- const language = resolveLanguage(file, languageRegistry, { language: effectiveConfig.language });
979
- const targets = await language.extract(file, {
980
- cwd: cwd$1,
981
- languageOptions: effectiveConfig.languageOptions,
982
- src
983
- });
984
- const registry = buildRuleRegistry(effectiveConfig);
985
- const ruleRuntimes = createRuleRuntimes({
986
- cwd: cwd$1,
987
- diagnostics,
988
- effectiveAgent: effectiveConfig.agent,
989
- effectiveSettings: effectiveConfig.settings,
990
- options,
991
- registry,
992
- setupConfig,
993
- src,
994
- usage
995
- });
996
- return {
997
- configHash: stableHash({
998
- language: effectiveConfig.language,
999
- languageOptions: effectiveConfig.languageOptions,
1000
- processor: effectiveConfig.processor,
1001
- resolvedLanguage: language.name,
1002
- rules: effectiveConfig.rules,
1003
- settings: effectiveConfig.settings
1004
- }),
1005
- file,
1006
- ruleRuntimes,
1007
- targets
1008
- };
1009
- }))).filter((file) => file !== void 0);
1010
- let planned = 0;
1011
- const counters = createRuleEndCounters();
1012
- const primaryError = createPrimaryErrorState();
1013
- let filePlans = [];
1014
- let runStartedAt;
1015
- let runError;
1016
- try {
1017
- filePlans = createPreparedFileExecutionPlans(files, cwd$1);
1018
- planned = calculatePlannedExecutions(filePlans);
1019
- runStartedAt = clock();
1020
- options.progress?.onRunStart?.({
1021
- files: filePlans.map((filePlan) => toProgressFilePath(filePlan, files.length)),
1022
- filesTotal: files.length,
1023
- planned,
1024
- rulesTotal: countEnabledRuleIds(files),
1025
- startedAt: runStartedAt
1026
- });
1027
- await runConcurrently(filePlans.filter((filePlan) => filePlan.targets.length > 0), resolveFileConcurrency(options.runner?.fileConcurrency), (filePlan) => executeFilePlan(filePlan, files.length, clock, counters, diagnostics, usage, cacheContext, options));
1028
- } catch (error) {
1029
- primaryError.set();
1030
- runError = error;
1031
- } finally {
1032
- await reconcileCache(filePlans, cacheContext);
1033
- emitCleanupProgress(() => options.progress?.onRunEnd?.({
1034
- ...counters.snapshot(planned),
1035
- diagnostics,
1036
- endedAt: clock(),
1037
- startedAt: runStartedAt ?? clock(),
1038
- usage: usage.toJSON()
1039
- }), primaryError);
1040
- }
1041
- const result = {
1042
- diagnostics,
1043
- usage: usage.toJSON()
1044
- };
1045
- if (runError) throw createAlintRunError(runError, result);
1046
- return result;
1047
- }
1048
- function addTokenCount(base, value) {
1049
- return typeof value === "number" && Number.isFinite(value) ? base + value : base;
1050
- }
1051
- function calculateFilePlanExecutions(filePlan) {
1052
- return filePlan.targets.reduce((total, target) => total + target.executions.length, 0);
1053
- }
1054
- function calculatePlannedExecutions(filePlans) {
1055
- return filePlans.reduce((total, filePlan) => total + filePlan.planned, 0);
1056
- }
1057
- function collectExecutionTargets(preparedFile) {
1058
- const targets = [];
1059
- for (const sourceTarget of preparedFile.targets) {
1060
- const executions = preparedFile.ruleRuntimes.map((runtime) => {
1061
- if (!runtime.handlers.onTarget) return;
1062
- return {
1063
- run: () => runtime.handlers.onTarget?.(sourceTarget),
1064
- runtime
1065
- };
1066
- }).filter((execution) => execution !== void 0);
1067
- if (executions.length === 0) continue;
1068
- targets.push({
1069
- configHash: preparedFile.configHash,
1070
- executions,
1071
- identity: sourceTarget.identity,
1072
- kind: sourceTarget.kind,
1073
- language: sourceTarget.language,
1074
- loc: sourceTarget.loc,
1075
- metadata: sourceTarget.metadata,
1076
- name: sourceTarget.name,
1077
- origin: sourceTarget.origin,
1078
- range: sourceTarget.range,
1079
- text: sourceTarget.text
1080
- });
1081
- }
1082
- return targets;
1083
- }
1084
- function countEnabledRuleIds(files) {
1085
- const ids = /* @__PURE__ */ new Set();
1086
- for (const file of files) for (const runtime of file.ruleRuntimes) ids.add(runtime.enabledRule.id);
1087
- return ids.size;
1088
- }
1089
- function createAlintRunError(error, result) {
1090
- if (error instanceof AlintRunError) return error;
1091
- if (error instanceof AlintRuleExecutionError) return new AlintRunError(error.message, result, {
1092
- cause: errorCauseFrom(error),
1093
- failure: error.failure
1094
- });
1095
- return new AlintRunError(errorMessageFrom(error) ?? String(error), result, { cause: error });
990
+ async function executeTargetPlans(options) {
991
+ for (const plan of options.plans) await executeTargetPlan(plan, options);
1096
992
  }
1097
- function createExecutionCacheKey(runtime, target, path, cacheContext) {
1098
- if (!cacheContext.enabled || !runtime.cacheable) return;
993
+ function createExecutionCacheKey(runtime, target, path, cache) {
994
+ if (!cache.enabled || !runtime.cacheable || target.cacheFilePaths.length === 0) return;
1099
995
  return createCacheKey({
1100
996
  alintVersion: version,
1101
997
  configHash: target.configHash,
1102
- filePath: normalizeCachePath(cacheContext.cwd, path.file.path),
1103
- modelHash: cacheContext.modelHash,
998
+ filePath: normalizeCachePath(cache.cwd, path.file.path),
999
+ modelHash: cache.modelHash,
1104
1000
  ruleHash: runtime.ruleHash,
1105
1001
  schemaVersion: 1,
1106
1002
  targetHash: createTargetHash(target),
@@ -1108,32 +1004,6 @@ function createExecutionCacheKey(runtime, target, path, cacheContext) {
1108
1004
  targetKind: target.kind
1109
1005
  });
1110
1006
  }
1111
- function createPreparedFileExecutionPlans(files, cwd) {
1112
- return files.map((preparedFile, fileOffset) => {
1113
- const targets = collectExecutionTargets(preparedFile);
1114
- const resolveTargetIdentity = createTargetIdentityResolver(targets.map((target) => toTargetIdentityInput(cwd, preparedFile.file.path, target)));
1115
- for (const target of targets) target.identity = resolveTargetIdentity(toTargetIdentityInput(cwd, preparedFile.file.path, target));
1116
- const filePlan = {
1117
- fileIndex: fileOffset + 1,
1118
- planned: 0,
1119
- preparedFile,
1120
- targets
1121
- };
1122
- filePlan.planned = calculateFilePlanExecutions(filePlan);
1123
- return filePlan;
1124
- });
1125
- }
1126
- function createPrimaryErrorState() {
1127
- let hasError = false;
1128
- return {
1129
- get hasError() {
1130
- return hasError;
1131
- },
1132
- set() {
1133
- hasError = true;
1134
- }
1135
- };
1136
- }
1137
1007
  function createProgressPath(filePath, ruleId, entry) {
1138
1008
  return {
1139
1009
  file: {
@@ -1155,13 +1025,596 @@ function createProgressPath(filePath, ruleId, entry) {
1155
1025
  }
1156
1026
  };
1157
1027
  }
1158
- function createRuleEndCounters() {
1159
- let cached = 0;
1160
- let completed = 0;
1161
- let errored = 0;
1162
- return {
1163
- cache() {
1164
- cached += 1;
1028
+ function createTargetHash(target) {
1029
+ return stableHash({
1030
+ language: target.language,
1031
+ loc: target.loc,
1032
+ metadata: target.metadata,
1033
+ name: target.name,
1034
+ origin: target.origin,
1035
+ range: target.range,
1036
+ text: target.text
1037
+ });
1038
+ }
1039
+ function emitErroredRuleEnd(progress, clock, path, startedAt, cache) {
1040
+ try {
1041
+ progress?.onRuleEnd?.({
1042
+ cache,
1043
+ endedAt: clock(),
1044
+ path,
1045
+ startedAt,
1046
+ state: "errored"
1047
+ });
1048
+ } catch {}
1049
+ }
1050
+ async function executeRule(execution, path, target, options) {
1051
+ const { cache, clock, counters, diagnostics, progress, usage } = options;
1052
+ const startedAt = clock();
1053
+ const cacheKey = createExecutionCacheKey(execution.runtime, target, path, cache);
1054
+ const cachedEntry = cacheKey ? cache.store.get(cacheKey) : void 0;
1055
+ progress?.onRuleStart?.({
1056
+ path,
1057
+ startedAt
1058
+ });
1059
+ if (cacheKey && cachedEntry) {
1060
+ rememberCacheEntry(cache, target.cacheFilePaths, cacheKey);
1061
+ try {
1062
+ replayCachedEntry(cachedEntry, path, diagnostics, usage, progress);
1063
+ } catch (error) {
1064
+ counters.error();
1065
+ emitErroredRuleEnd(progress, clock, path, startedAt, "hit");
1066
+ throw new AlintRuleExecutionError(error, path);
1067
+ }
1068
+ counters.cache();
1069
+ progress?.onRuleEnd?.({
1070
+ cache: "hit",
1071
+ endedAt: clock(),
1072
+ path,
1073
+ startedAt,
1074
+ state: "completed"
1075
+ });
1076
+ return;
1077
+ }
1078
+ let handlerError;
1079
+ let handlerFailed = false;
1080
+ const cacheDiagnostics = cacheKey ? [] : void 0;
1081
+ const cacheUsage = cacheKey ? [] : void 0;
1082
+ try {
1083
+ await execution.runtime.executionState.run({
1084
+ activeFilePath: target.activeFilePath,
1085
+ cacheDiagnostics,
1086
+ cacheUsage,
1087
+ progressPath: path
1088
+ }, execution.run);
1089
+ } catch (error) {
1090
+ handlerError = error;
1091
+ handlerFailed = true;
1092
+ }
1093
+ if (handlerFailed) {
1094
+ counters.error();
1095
+ emitErroredRuleEnd(progress, clock, path, startedAt, "miss");
1096
+ throw new AlintRuleExecutionError(handlerError, path);
1097
+ }
1098
+ if (cacheKey) {
1099
+ cache.store.set(cacheKey, {
1100
+ diagnostics: cacheDiagnostics ?? [],
1101
+ filePath: normalizeCachePath(cache.cwd, path.file.path),
1102
+ fingerprint: {
1103
+ alintVersion: version,
1104
+ configHash: target.configHash,
1105
+ modelHash: cache.modelHash,
1106
+ ruleHash: execution.runtime.ruleHash
1107
+ },
1108
+ target: {
1109
+ hash: createTargetHash(target),
1110
+ identity: target.identity,
1111
+ kind: target.kind,
1112
+ loc: target.loc,
1113
+ name: target.name,
1114
+ range: target.range
1115
+ },
1116
+ usage: cacheUsage ?? []
1117
+ });
1118
+ rememberCacheEntry(cache, target.cacheFilePaths, cacheKey);
1119
+ }
1120
+ counters.complete();
1121
+ progress?.onRuleEnd?.({
1122
+ cache: "miss",
1123
+ endedAt: clock(),
1124
+ path,
1125
+ startedAt,
1126
+ state: "completed"
1127
+ });
1128
+ }
1129
+ async function executeTargetPlan(plan, options) {
1130
+ const { clock, filesTotal, progress } = options;
1131
+ const fileStartedAt = clock();
1132
+ const fileProgress = {
1133
+ index: plan.fileIndex,
1134
+ path: plan.path,
1135
+ planned: plan.planned,
1136
+ total: filesTotal
1137
+ };
1138
+ if (plan.emitFileProgress) progress?.onFileStart?.({
1139
+ file: fileProgress,
1140
+ startedAt: fileStartedAt
1141
+ });
1142
+ let planError;
1143
+ let planFailed = false;
1144
+ try {
1145
+ for (const [targetOffset, target] of plan.targets.entries()) {
1146
+ const targetPath = createProgressPath(plan.path, target.executions[0]?.runtime.enabledRule.id ?? "", {
1147
+ fileIndex: plan.fileIndex,
1148
+ filePlanned: plan.planned,
1149
+ fileTotal: filesTotal,
1150
+ ruleIndex: 1,
1151
+ ruleTotal: target.executions.length,
1152
+ targetIndex: targetOffset + 1,
1153
+ targetKind: target.kind,
1154
+ targetName: target.name,
1155
+ targetTotal: plan.targets.length
1156
+ });
1157
+ const targetStartedAt = clock();
1158
+ progress?.onTargetStart?.({
1159
+ path: targetPath,
1160
+ startedAt: targetStartedAt
1161
+ });
1162
+ let targetError;
1163
+ let targetFailed = false;
1164
+ try {
1165
+ for (const [executionOffset, execution] of target.executions.entries()) await executeRule(execution, createProgressPath(plan.path, execution.runtime.enabledRule.id, {
1166
+ fileIndex: plan.fileIndex,
1167
+ filePlanned: plan.planned,
1168
+ fileTotal: filesTotal,
1169
+ ruleIndex: executionOffset + 1,
1170
+ ruleTotal: target.executions.length,
1171
+ targetIndex: targetOffset + 1,
1172
+ targetKind: target.kind,
1173
+ targetName: target.name,
1174
+ targetTotal: plan.targets.length
1175
+ }), target, options);
1176
+ } catch (error) {
1177
+ targetError = error;
1178
+ targetFailed = true;
1179
+ }
1180
+ try {
1181
+ progress?.onTargetEnd?.({
1182
+ endedAt: clock(),
1183
+ path: targetPath,
1184
+ startedAt: targetStartedAt
1185
+ });
1186
+ } catch (error) {
1187
+ if (!targetFailed) {
1188
+ targetError = error;
1189
+ targetFailed = true;
1190
+ }
1191
+ }
1192
+ if (targetFailed) throw targetError;
1193
+ }
1194
+ } catch (error) {
1195
+ planError = error;
1196
+ planFailed = true;
1197
+ }
1198
+ if (plan.emitFileProgress) try {
1199
+ progress?.onFileEnd?.({
1200
+ endedAt: clock(),
1201
+ file: fileProgress,
1202
+ startedAt: fileStartedAt
1203
+ });
1204
+ } catch (error) {
1205
+ if (!planFailed) {
1206
+ planError = error;
1207
+ planFailed = true;
1208
+ }
1209
+ }
1210
+ if (planFailed) throw planError;
1211
+ }
1212
+ function rememberCacheEntry(cache, filePaths, cacheKey) {
1213
+ for (const filePath of filePaths) {
1214
+ const normalizedPath = normalizeCachePath(cache.cwd, filePath);
1215
+ const entries = cache.fileEntryKeys.get(normalizedPath) ?? /* @__PURE__ */ new Set();
1216
+ entries.add(cacheKey);
1217
+ cache.fileEntryKeys.set(normalizedPath, entries);
1218
+ }
1219
+ }
1220
+ function replayCachedEntry(entry, path, diagnostics, usage, progress) {
1221
+ for (const cachedDiagnostic of entry.diagnostics) {
1222
+ const diagnostic = {
1223
+ ...cachedDiagnostic,
1224
+ cached: true
1225
+ };
1226
+ diagnostics.push(diagnostic);
1227
+ progress?.onDiagnostic?.({
1228
+ diagnostic,
1229
+ diagnostics: [...diagnostics],
1230
+ path
1231
+ });
1232
+ }
1233
+ for (const cachedUsage of entry.usage) {
1234
+ const record = usage.recordCached({ ...cachedUsage });
1235
+ progress?.onUsage?.({
1236
+ path,
1237
+ record,
1238
+ total: usage.toJSON()
1239
+ });
1240
+ }
1241
+ }
1242
+ //#endregion
1243
+ //#region src/core/targets/project.ts
1244
+ function createProjectExecutionPlan(options) {
1245
+ const project = createProjectTarget(options.root, options.files);
1246
+ const executions = options.ruleRuntimes.map((runtime) => {
1247
+ if (runtime.handlers.onTargetWith) return {
1248
+ run: () => runtime.handlers.onTargetWith?.(project),
1249
+ runtime
1250
+ };
1251
+ if (runtime.handlers.onTargetProject) return {
1252
+ run: () => runtime.handlers.onTargetProject?.(project),
1253
+ runtime
1254
+ };
1255
+ }).filter((execution) => execution !== void 0);
1256
+ if (executions.length === 0) return;
1257
+ const target = {
1258
+ cacheFilePaths: options.files.map((file) => file.file.path),
1259
+ configHash: options.configHash,
1260
+ executions,
1261
+ identity: "project",
1262
+ kind: "project",
1263
+ language: "project",
1264
+ text: stableHash({
1265
+ files: options.files.map((file) => ({
1266
+ configHash: file.configHash,
1267
+ contentHash: hashText(file.file.text),
1268
+ path: normalizeCachePath(options.root, file.file.path)
1269
+ })).sort((left, right) => left.path.localeCompare(right.path)),
1270
+ targets: project.targets.map((target) => ({
1271
+ filePath: normalizeCachePath(options.root, target.file.path),
1272
+ identity: target.identity,
1273
+ kind: target.kind,
1274
+ language: target.language,
1275
+ loc: target.loc,
1276
+ metadata: target.metadata,
1277
+ name: target.name,
1278
+ origin: target.origin,
1279
+ range: target.range,
1280
+ text: target.text
1281
+ })),
1282
+ tree: createProjectTreeShape(options.files, options.root)
1283
+ })
1284
+ };
1285
+ return {
1286
+ emitFileProgress: false,
1287
+ fileIndex: 0,
1288
+ path: options.root,
1289
+ planned: executions.length,
1290
+ targets: [target]
1291
+ };
1292
+ }
1293
+ function createProjectTarget(root, files) {
1294
+ const preparedFiles = [...files].sort((left, right) => left.file.path.localeCompare(right.file.path));
1295
+ return {
1296
+ files: preparedFiles.map((file) => file.file),
1297
+ kind: "project",
1298
+ root,
1299
+ targets: preparedFiles.flatMap((file) => file.targets)
1300
+ };
1301
+ }
1302
+ function createProjectTreeShape(files, root) {
1303
+ const directories = /* @__PURE__ */ new Set();
1304
+ const paths = files.map((file) => normalizeCachePath(root, file.file.path)).sort();
1305
+ for (const path of paths) {
1306
+ const parts = path.split("/");
1307
+ for (let index = 1; index < parts.length; index += 1) directories.add(parts.slice(0, index).join("/"));
1308
+ }
1309
+ return {
1310
+ directories: [...directories].sort(),
1311
+ files: paths
1312
+ };
1313
+ }
1314
+ //#endregion
1315
+ //#region src/core/targets/source.ts
1316
+ function createSourceExecutionPlans(files, cwd) {
1317
+ return files.map((preparedFile, fileOffset) => {
1318
+ const targets = collectExecutionTargets(preparedFile);
1319
+ const resolveTargetIdentity = createTargetIdentityResolver(targets.map((target) => toTargetIdentityInput(cwd, preparedFile.file.path, target)));
1320
+ for (const target of targets) target.identity = resolveTargetIdentity(toTargetIdentityInput(cwd, preparedFile.file.path, target));
1321
+ const filePlan = {
1322
+ emitFileProgress: true,
1323
+ fileIndex: fileOffset + 1,
1324
+ path: preparedFile.file.path,
1325
+ planned: 0,
1326
+ preparedFile,
1327
+ targets
1328
+ };
1329
+ filePlan.planned = calculateFilePlanExecutions(filePlan);
1330
+ return filePlan;
1331
+ });
1332
+ }
1333
+ function calculateFilePlanExecutions(filePlan) {
1334
+ return filePlan.targets.reduce((total, target) => total + target.executions.length, 0);
1335
+ }
1336
+ function collectExecutionTargets(preparedFile) {
1337
+ const targets = [];
1338
+ for (const sourceTarget of preparedFile.targets) {
1339
+ const executions = preparedFile.ruleRuntimes.map((runtime) => {
1340
+ return createSourceTargetExecution(runtime, sourceTarget);
1341
+ }).filter((execution) => execution !== void 0);
1342
+ if (executions.length === 0) continue;
1343
+ targets.push({
1344
+ activeFilePath: preparedFile.file.path,
1345
+ cacheFilePaths: [preparedFile.file.path],
1346
+ configHash: preparedFile.configHash,
1347
+ executions,
1348
+ identity: sourceTarget.identity,
1349
+ kind: sourceTarget.kind,
1350
+ language: sourceTarget.language,
1351
+ loc: sourceTarget.loc,
1352
+ metadata: sourceTarget.metadata,
1353
+ name: sourceTarget.name,
1354
+ origin: sourceTarget.origin,
1355
+ range: sourceTarget.range,
1356
+ text: sourceTarget.text
1357
+ });
1358
+ }
1359
+ return targets;
1360
+ }
1361
+ function createSourceTargetExecution(runtime, target) {
1362
+ if (runtime.handlers.onTargetWith) return {
1363
+ run: () => runtime.handlers.onTargetWith?.(target),
1364
+ runtime
1365
+ };
1366
+ if (target.kind === "class" && runtime.handlers.onTargetClass) return {
1367
+ run: () => runtime.handlers.onTargetClass?.(target),
1368
+ runtime
1369
+ };
1370
+ if (target.kind === "file" && runtime.handlers.onTargetFile) return {
1371
+ run: () => runtime.handlers.onTargetFile?.(target),
1372
+ runtime
1373
+ };
1374
+ if (target.kind === "function" && runtime.handlers.onTargetFunction) return {
1375
+ run: () => runtime.handlers.onTargetFunction?.(target),
1376
+ runtime
1377
+ };
1378
+ }
1379
+ function toTargetIdentityInput(cwd, filePath, target) {
1380
+ return {
1381
+ filePath: target.kind === "file" ? normalizeCachePath(cwd, filePath) : void 0,
1382
+ identity: target.identity,
1383
+ kind: target.kind,
1384
+ name: target.name,
1385
+ range: target.range
1386
+ };
1387
+ }
1388
+ //#endregion
1389
+ //#region src/core/run.ts
1390
+ var AlintRunError = class extends Error {
1391
+ failure;
1392
+ result;
1393
+ constructor(message, result, options = {}) {
1394
+ super(message, { cause: options.cause });
1395
+ this.name = "AlintRunError";
1396
+ this.failure = options.failure;
1397
+ this.result = result;
1398
+ }
1399
+ };
1400
+ async function runAlint(options = {}) {
1401
+ const cwd$1 = options.cwd ?? cwd();
1402
+ const config = options.config ?? [];
1403
+ const setupConfig = options.setupConfig ?? {
1404
+ providers: [],
1405
+ version: 1
1406
+ };
1407
+ const clock = Date.now;
1408
+ const diagnostics = [];
1409
+ const usage = createUsageAccumulator();
1410
+ const src = createSourceRuntime();
1411
+ const normalizedCacheConfig = normalizeRunnerCacheConfig(options.runner?.cache, cwd$1);
1412
+ const cacheStore = await createCacheStore({
1413
+ cwd: cwd$1,
1414
+ enabled: normalizedCacheConfig.enabled,
1415
+ location: normalizedCacheConfig.location
1416
+ });
1417
+ const cacheContext = {
1418
+ cwd: cwd$1,
1419
+ enabled: normalizedCacheConfig.enabled,
1420
+ fileEntryKeys: /* @__PURE__ */ new Map(),
1421
+ modelHash: stableHash({
1422
+ modelOverride: options.modelOverride,
1423
+ outputLanguage: options.outputLanguage,
1424
+ setupConfig
1425
+ }),
1426
+ store: cacheStore
1427
+ };
1428
+ const pendingFiles = [...options.files ?? []];
1429
+ const pendingDirectories = [...options.directories ?? []];
1430
+ const files = (await Promise.all(pendingFiles.map(async (filePath) => {
1431
+ const file = await src.readFile(resolve(cwd$1, filePath));
1432
+ const resolvedConfig = resolveConfigForFile(file.path, config, { cwd: cwd$1 });
1433
+ if (resolvedConfig.ignored) return;
1434
+ const effectiveConfig = resolvedConfig.config;
1435
+ const languageRegistry = createBuiltInLanguageRegistry();
1436
+ for (const plugin of Object.values(effectiveConfig.plugins)) for (const language of Object.values(plugin.languages ?? {})) registerLanguage(languageRegistry, language);
1437
+ const language = resolveLanguage(file, languageRegistry, { language: effectiveConfig.language });
1438
+ const targets = await language.extract(file, {
1439
+ cwd: cwd$1,
1440
+ languageOptions: effectiveConfig.languageOptions,
1441
+ src
1442
+ });
1443
+ const registry = buildRuleRegistry(effectiveConfig);
1444
+ const ruleRuntimes = createRuleRuntimes({
1445
+ cwd: cwd$1,
1446
+ diagnostics,
1447
+ effectiveAgent: effectiveConfig.agent,
1448
+ effectiveSettings: effectiveConfig.settings,
1449
+ options,
1450
+ registry,
1451
+ setupConfig,
1452
+ src,
1453
+ usage
1454
+ });
1455
+ return {
1456
+ configHash: stableHash({
1457
+ language: effectiveConfig.language,
1458
+ languageOptions: effectiveConfig.languageOptions,
1459
+ processor: effectiveConfig.processor,
1460
+ resolvedLanguage: language.name,
1461
+ settings: effectiveConfig.settings
1462
+ }),
1463
+ file,
1464
+ ruleRuntimes,
1465
+ targets
1466
+ };
1467
+ }))).filter((file) => file !== void 0);
1468
+ const directories = pendingDirectories.map((directoryPath) => {
1469
+ const target = {
1470
+ kind: "directory",
1471
+ path: resolve(cwd$1, directoryPath)
1472
+ };
1473
+ const resolvedConfig = resolveConfigForDirectory(target.path, config, { cwd: cwd$1 });
1474
+ if (resolvedConfig.ignored) return;
1475
+ const effectiveConfig = resolvedConfig.config;
1476
+ const registry = buildRuleRegistry(effectiveConfig);
1477
+ return {
1478
+ configHash: stableHash({ settings: effectiveConfig.settings }),
1479
+ ruleRuntimes: createRuleRuntimes({
1480
+ cwd: cwd$1,
1481
+ diagnostics,
1482
+ effectiveAgent: effectiveConfig.agent,
1483
+ effectiveSettings: effectiveConfig.settings,
1484
+ options,
1485
+ registry,
1486
+ setupConfig,
1487
+ src,
1488
+ usage
1489
+ }),
1490
+ target
1491
+ };
1492
+ }).filter((directory) => directory !== void 0);
1493
+ const resolvedProjectConfig = resolveConfigForProject(cwd$1, config, { cwd: cwd$1 });
1494
+ const projectConfig = resolvedProjectConfig.config;
1495
+ const projectRuleRuntimes = resolvedProjectConfig.ignored ? [] : createRuleRuntimes({
1496
+ cwd: cwd$1,
1497
+ diagnostics,
1498
+ effectiveAgent: projectConfig.agent,
1499
+ effectiveSettings: projectConfig.settings,
1500
+ options,
1501
+ registry: buildRuleRegistry(projectConfig),
1502
+ setupConfig,
1503
+ src,
1504
+ usage
1505
+ });
1506
+ let planned = 0;
1507
+ const counters = createRuleEndCounters();
1508
+ let filePlans = [];
1509
+ let directoryPlans = [];
1510
+ let projectPlan;
1511
+ let runStartedAt;
1512
+ let runError;
1513
+ try {
1514
+ filePlans = createSourceExecutionPlans(files, cwd$1);
1515
+ directoryPlans = createDirectoryExecutionPlans(directories, filePlans.length);
1516
+ projectPlan = resolvedProjectConfig.ignored ? void 0 : createProjectExecutionPlan({
1517
+ configHash: stableHash({ settings: projectConfig.settings }),
1518
+ files,
1519
+ root: cwd$1,
1520
+ ruleRuntimes: projectRuleRuntimes
1521
+ });
1522
+ const activeDirectoryPlans = directoryPlans.filter((plan) => plan.targets.length > 0).map((plan, directoryOffset) => ({
1523
+ ...plan,
1524
+ fileIndex: filePlans.length + directoryOffset + 1
1525
+ }));
1526
+ const inputsTotal = files.length + activeDirectoryPlans.length;
1527
+ planned = calculatePlannedExecutions([...filePlans, ...directoryPlans]) + (projectPlan?.planned ?? 0);
1528
+ runStartedAt = clock();
1529
+ options.progress?.onRunStart?.({
1530
+ files: [...filePlans, ...activeDirectoryPlans].map((plan) => toProgressFilePath(plan, inputsTotal)),
1531
+ filesTotal: inputsTotal,
1532
+ planned,
1533
+ rulesTotal: countEnabledRuleIds(files, directories, projectRuleRuntimes),
1534
+ startedAt: runStartedAt
1535
+ });
1536
+ await runConcurrently(filePlans.filter((filePlan) => filePlan.targets.length > 0), resolveFileConcurrency(options.runner?.fileConcurrency), (filePlan) => executeTargetPlans({
1537
+ cache: cacheContext,
1538
+ clock,
1539
+ counters,
1540
+ diagnostics,
1541
+ filesTotal: inputsTotal,
1542
+ plans: [filePlan],
1543
+ progress: options.progress,
1544
+ usage
1545
+ }));
1546
+ await runConcurrently(activeDirectoryPlans, resolveFileConcurrency(options.runner?.fileConcurrency), (directoryPlan) => executeTargetPlans({
1547
+ cache: cacheContext,
1548
+ clock,
1549
+ counters,
1550
+ diagnostics,
1551
+ filesTotal: inputsTotal,
1552
+ plans: [directoryPlan],
1553
+ progress: options.progress,
1554
+ usage
1555
+ }));
1556
+ if (projectPlan) await executeTargetPlans({
1557
+ cache: cacheContext,
1558
+ clock,
1559
+ counters,
1560
+ diagnostics,
1561
+ filesTotal: inputsTotal,
1562
+ plans: [projectPlan],
1563
+ progress: options.progress,
1564
+ usage
1565
+ });
1566
+ } catch (error) {
1567
+ runError = error;
1568
+ } finally {
1569
+ await reconcileCache(filePlans, cacheContext);
1570
+ try {
1571
+ options.progress?.onRunEnd?.({
1572
+ ...counters.snapshot(planned),
1573
+ diagnostics,
1574
+ endedAt: clock(),
1575
+ startedAt: runStartedAt ?? clock(),
1576
+ usage: usage.toJSON()
1577
+ });
1578
+ } catch (error) {
1579
+ runError ??= error;
1580
+ }
1581
+ }
1582
+ const result = {
1583
+ diagnostics,
1584
+ execution: counters.snapshot(planned),
1585
+ usage: usage.toJSON()
1586
+ };
1587
+ if (runError) throw createAlintRunError(runError, result);
1588
+ return result;
1589
+ }
1590
+ function addTokenCount(base, value) {
1591
+ return typeof value === "number" && Number.isFinite(value) ? base + value : base;
1592
+ }
1593
+ function calculatePlannedExecutions(filePlans) {
1594
+ return filePlans.reduce((total, filePlan) => total + filePlan.planned, 0);
1595
+ }
1596
+ function countEnabledRuleIds(files, directories, projectRuleRuntimes) {
1597
+ const ids = /* @__PURE__ */ new Set();
1598
+ for (const file of files) for (const runtime of file.ruleRuntimes) ids.add(runtime.enabledRule.id);
1599
+ for (const directory of directories) for (const runtime of directory.ruleRuntimes) ids.add(runtime.enabledRule.id);
1600
+ for (const runtime of projectRuleRuntimes) ids.add(runtime.enabledRule.id);
1601
+ return ids.size;
1602
+ }
1603
+ function createAlintRunError(error, result) {
1604
+ if (error instanceof AlintRunError) return error;
1605
+ if (error instanceof AlintRuleExecutionError) return new AlintRunError(error.message, result, {
1606
+ cause: errorCauseFrom(error),
1607
+ failure: error.failure
1608
+ });
1609
+ return new AlintRunError(errorMessageFrom(error) ?? String(error), result, { cause: error });
1610
+ }
1611
+ function createRuleEndCounters() {
1612
+ let cached = 0;
1613
+ let completed = 0;
1614
+ let errored = 0;
1615
+ return {
1616
+ cache() {
1617
+ cached += 1;
1165
1618
  },
1166
1619
  complete() {
1167
1620
  completed += 1;
@@ -1247,6 +1700,7 @@ function createRuleRuntimes(options) {
1247
1700
  handlers: enabledRule.rule.create(context),
1248
1701
  ruleHash: stableHash({
1249
1702
  cache: enabledRule.rule.cache ?? true,
1703
+ cacheKey: enabledRule.rule.cacheKey,
1250
1704
  create: String(enabledRule.rule.create),
1251
1705
  id: enabledRule.id,
1252
1706
  localId: enabledRule.localId,
@@ -1256,18 +1710,26 @@ function createRuleRuntimes(options) {
1256
1710
  };
1257
1711
  });
1258
1712
  }
1259
- function createTargetHash(target) {
1260
- return stableHash({
1261
- language: target.language,
1262
- loc: target.loc,
1263
- metadata: target.metadata,
1264
- name: target.name,
1265
- origin: target.origin,
1266
- range: target.range,
1267
- text: target.text
1268
- });
1269
- }
1270
1713
  function createUsageAccumulator() {
1714
+ const live = createUsageTotals();
1715
+ const cached = createUsageTotals();
1716
+ return {
1717
+ record(record) {
1718
+ return live.record(record);
1719
+ },
1720
+ recordCached(record) {
1721
+ return cached.record(record);
1722
+ },
1723
+ toJSON() {
1724
+ const cachedUsage = cached.toJSON();
1725
+ return {
1726
+ ...live.toJSON(),
1727
+ ...cachedUsage.records.length > 0 ? { cached: cachedUsage } : {}
1728
+ };
1729
+ }
1730
+ };
1731
+ }
1732
+ function createUsageTotals() {
1271
1733
  const records = [];
1272
1734
  let inputTokens = 0;
1273
1735
  let outputTokens = 0;
@@ -1290,175 +1752,6 @@ function createUsageAccumulator() {
1290
1752
  }
1291
1753
  };
1292
1754
  }
1293
- function emitCleanupProgress(callback, primaryError) {
1294
- try {
1295
- callback();
1296
- } catch (error) {
1297
- if (!primaryError.hasError) throw error;
1298
- }
1299
- }
1300
- async function executeFilePlan(filePlan, filesTotal, clock, counters, diagnostics, usage, cacheContext, options) {
1301
- const preparedFile = filePlan.preparedFile;
1302
- const fileStartedAt = clock();
1303
- const fileProgress = {
1304
- index: filePlan.fileIndex,
1305
- path: preparedFile.file.path,
1306
- planned: filePlan.planned,
1307
- total: filesTotal
1308
- };
1309
- options.progress?.onFileStart?.({
1310
- file: fileProgress,
1311
- startedAt: fileStartedAt
1312
- });
1313
- const fileError = createPrimaryErrorState();
1314
- try {
1315
- for (const [targetOffset, target] of filePlan.targets.entries()) {
1316
- const targetPath = createProgressPath(preparedFile.file.path, target.executions[0]?.runtime.enabledRule.id ?? "", {
1317
- fileIndex: filePlan.fileIndex,
1318
- filePlanned: filePlan.planned,
1319
- fileTotal: filesTotal,
1320
- ruleIndex: 1,
1321
- ruleTotal: target.executions.length,
1322
- targetIndex: targetOffset + 1,
1323
- targetKind: target.kind,
1324
- targetName: target.name,
1325
- targetTotal: filePlan.targets.length
1326
- });
1327
- const targetError = createPrimaryErrorState();
1328
- const targetStartedAt = clock();
1329
- options.progress?.onTargetStart?.({
1330
- path: targetPath,
1331
- startedAt: targetStartedAt
1332
- });
1333
- try {
1334
- for (const [executionOffset, execution] of target.executions.entries()) await executeProgressTarget(execution, createProgressPath(preparedFile.file.path, execution.runtime.enabledRule.id, {
1335
- fileIndex: filePlan.fileIndex,
1336
- filePlanned: filePlan.planned,
1337
- fileTotal: filesTotal,
1338
- ruleIndex: executionOffset + 1,
1339
- ruleTotal: target.executions.length,
1340
- targetIndex: targetOffset + 1,
1341
- targetKind: target.kind,
1342
- targetName: target.name,
1343
- targetTotal: filePlan.targets.length
1344
- }), target, clock, counters, diagnostics, usage, cacheContext, options);
1345
- } catch (error) {
1346
- targetError.set();
1347
- throw error;
1348
- } finally {
1349
- emitCleanupProgress(() => options.progress?.onTargetEnd?.({
1350
- endedAt: clock(),
1351
- path: targetPath,
1352
- startedAt: targetStartedAt
1353
- }), targetError);
1354
- }
1355
- }
1356
- } catch (error) {
1357
- fileError.set();
1358
- throw error;
1359
- } finally {
1360
- emitCleanupProgress(() => options.progress?.onFileEnd?.({
1361
- endedAt: clock(),
1362
- file: fileProgress,
1363
- startedAt: fileStartedAt
1364
- }), fileError);
1365
- }
1366
- }
1367
- async function executeProgressTarget(execution, path, target, clock, counters, diagnostics, usage, cacheContext, options) {
1368
- const startedAt = clock();
1369
- const cacheKey = createExecutionCacheKey(execution.runtime, target, path, cacheContext);
1370
- const cachedEntry = cacheKey && cacheContext.enabled && execution.runtime.cacheable ? cacheContext.store.get(cacheKey) : void 0;
1371
- options.progress?.onRuleStart?.({
1372
- path,
1373
- startedAt
1374
- });
1375
- if (cacheKey && cachedEntry) {
1376
- rememberFileCacheEntry(cacheContext, path.file.path, cacheKey);
1377
- try {
1378
- replayCachedEntry(cachedEntry, path, diagnostics, usage, options);
1379
- } catch (error) {
1380
- counters.error();
1381
- try {
1382
- options.progress?.onRuleEnd?.({
1383
- cache: "hit",
1384
- endedAt: clock(),
1385
- path,
1386
- startedAt,
1387
- state: "errored"
1388
- });
1389
- } catch {}
1390
- throw new AlintRuleExecutionError(error, path);
1391
- }
1392
- counters.cache();
1393
- options.progress?.onRuleEnd?.({
1394
- cache: "hit",
1395
- endedAt: clock(),
1396
- path,
1397
- startedAt,
1398
- state: "completed"
1399
- });
1400
- return;
1401
- }
1402
- let handlerError;
1403
- let handlerSucceeded = false;
1404
- const cacheDiagnostics = cacheKey ? [] : void 0;
1405
- const cacheUsage = cacheKey ? [] : void 0;
1406
- try {
1407
- await execution.runtime.executionState.run({
1408
- activeFilePath: path.file.path,
1409
- cacheDiagnostics,
1410
- cacheUsage,
1411
- progressPath: path
1412
- }, execution.run);
1413
- handlerSucceeded = true;
1414
- } catch (error) {
1415
- handlerError = error;
1416
- }
1417
- if (handlerSucceeded) {
1418
- if (cacheKey && cacheContext.enabled && execution.runtime.cacheable) {
1419
- cacheContext.store.set(cacheKey, {
1420
- diagnostics: cacheDiagnostics ?? [],
1421
- filePath: normalizeCachePath(cacheContext.cwd, path.file.path),
1422
- fingerprint: {
1423
- alintVersion: version,
1424
- configHash: target.configHash,
1425
- modelHash: cacheContext.modelHash,
1426
- ruleHash: execution.runtime.ruleHash
1427
- },
1428
- target: {
1429
- hash: createTargetHash(target),
1430
- identity: target.identity,
1431
- kind: target.kind,
1432
- loc: target.loc,
1433
- name: target.name,
1434
- range: target.range
1435
- },
1436
- usage: cacheUsage ?? []
1437
- });
1438
- rememberFileCacheEntry(cacheContext, path.file.path, cacheKey);
1439
- }
1440
- counters.complete();
1441
- options.progress?.onRuleEnd?.({
1442
- cache: "miss",
1443
- endedAt: clock(),
1444
- path,
1445
- startedAt,
1446
- state: "completed"
1447
- });
1448
- return;
1449
- }
1450
- counters.error();
1451
- try {
1452
- options.progress?.onRuleEnd?.({
1453
- cache: "miss",
1454
- endedAt: clock(),
1455
- path,
1456
- startedAt,
1457
- state: "errored"
1458
- });
1459
- } catch {}
1460
- throw new AlintRuleExecutionError(handlerError, path);
1461
- }
1462
1755
  function mergeCapabilities(base, extra) {
1463
1756
  if (!base && !extra) return;
1464
1757
  return [.../* @__PURE__ */ new Set([...base ?? [], ...extra ?? []])];
@@ -1493,31 +1786,6 @@ async function reconcileCache(filePlans, cacheContext) {
1493
1786
  await cacheContext.store.reconcile();
1494
1787
  } catch {}
1495
1788
  }
1496
- function rememberFileCacheEntry(cacheContext, filePath, cacheKey) {
1497
- const normalizedPath = normalizeCachePath(cacheContext.cwd, filePath);
1498
- const entries = cacheContext.fileEntryKeys.get(normalizedPath) ?? /* @__PURE__ */ new Set();
1499
- entries.add(cacheKey);
1500
- cacheContext.fileEntryKeys.set(normalizedPath, entries);
1501
- }
1502
- function replayCachedEntry(entry, path, diagnostics, usage, options) {
1503
- for (const cachedDiagnostic of entry.diagnostics) {
1504
- const diagnostic = { ...cachedDiagnostic };
1505
- diagnostics.push(diagnostic);
1506
- options.progress?.onDiagnostic?.({
1507
- diagnostic,
1508
- diagnostics: [...diagnostics],
1509
- path
1510
- });
1511
- }
1512
- for (const cachedUsage of entry.usage) {
1513
- const record = usage.record({ ...cachedUsage });
1514
- options.progress?.onUsage?.({
1515
- path,
1516
- record,
1517
- total: usage.toJSON()
1518
- });
1519
- }
1520
- }
1521
1789
  function resolveFileConcurrency(fileConcurrency) {
1522
1790
  return fileConcurrency ?? 1;
1523
1791
  }
@@ -1551,20 +1819,11 @@ function toDiagnosticModel(model, request) {
1551
1819
  function toProgressFilePath(filePlan, filesTotal) {
1552
1820
  return {
1553
1821
  index: filePlan.fileIndex,
1554
- path: filePlan.preparedFile.file.path,
1822
+ path: filePlan.path,
1555
1823
  planned: filePlan.planned,
1556
1824
  total: filesTotal
1557
1825
  };
1558
1826
  }
1559
- function toTargetIdentityInput(cwd, filePath, target) {
1560
- return {
1561
- filePath: target.kind === "file" ? normalizeCachePath(cwd, filePath) : void 0,
1562
- identity: target.identity,
1563
- kind: target.kind,
1564
- name: target.name,
1565
- range: target.range
1566
- };
1567
- }
1568
1827
  //#endregion
1569
1828
  //#region src/dsl/define.ts
1570
1829
  function defineConfig(config) {
@@ -1577,4 +1836,4 @@ function defineRule(rule) {
1577
1836
  return rule;
1578
1837
  }
1579
1838
  //#endregion
1580
- export { AlintRunError, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceFile, createSourceRuntime, defineConfig, definePlugin, defineRule, extractJsSourceTargets, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, registerLanguage, resolveConfigForFile, resolveLanguage, resolveModel, runAlint, sliceLines, sliceRange };
1839
+ export { AlintRunError, buildRuleRegistry, createBuiltInLanguageRegistry, createSourceFile, createSourceRuntime, defineConfig, definePlugin, defineRule, extractJsSourceTargets, hasDiscoveryFilePatterns, matchesDiscoveryFile, normalizeConfig, registerLanguage, resolveConfigForDirectory, resolveConfigForFile, resolveConfigForProject, resolveLanguage, resolveModel, runAlint, sliceLines, sliceRange };