@packmind/cli 0.5.0 → 0.6.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/main.cjs +794 -207
- package/package.json +1 -1
package/main.cjs
CHANGED
|
@@ -38,7 +38,7 @@ var require_package = __commonJS({
|
|
|
38
38
|
"apps/cli/package.json"(exports2, module2) {
|
|
39
39
|
module2.exports = {
|
|
40
40
|
name: "@packmind/cli",
|
|
41
|
-
version: "0.
|
|
41
|
+
version: "0.6.0",
|
|
42
42
|
description: "A command-line interface for Packmind linting and code quality checks",
|
|
43
43
|
private: false,
|
|
44
44
|
bin: {
|
|
@@ -76,7 +76,6 @@ var require_package = __commonJS({
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
// apps/cli/src/main.ts
|
|
79
|
-
var import_chalk2 = __toESM(require("chalk"));
|
|
80
79
|
var import_cmd_ts3 = require("cmd-ts");
|
|
81
80
|
|
|
82
81
|
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
@@ -258,6 +257,31 @@ var createRuleId = brandedIdFactory();
|
|
|
258
257
|
// packages/types/src/standards/RuleExampleId.ts
|
|
259
258
|
var createRuleExampleId = brandedIdFactory();
|
|
260
259
|
|
|
260
|
+
// packages/types/src/events/PackmindEvent.ts
|
|
261
|
+
var PackmindEvent = class {
|
|
262
|
+
constructor(payload) {
|
|
263
|
+
this.payload = payload;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get the event name from the static property.
|
|
267
|
+
* Used internally by the event emitter.
|
|
268
|
+
*/
|
|
269
|
+
get name() {
|
|
270
|
+
return this.constructor.eventName;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// packages/types/src/events/UserEvent.ts
|
|
275
|
+
var UserEvent = class extends PackmindEvent {
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// packages/types/src/standards/events/StandardUpdatedEvent.ts
|
|
279
|
+
var StandardUpdatedEvent = class extends UserEvent {
|
|
280
|
+
static {
|
|
281
|
+
this.eventName = "standards.standard.updated";
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
261
285
|
// packages/types/src/languages/ProgrammingLanguage.ts
|
|
262
286
|
var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage4) => {
|
|
263
287
|
ProgrammingLanguage4["JAVASCRIPT"] = "JAVASCRIPT";
|
|
@@ -793,36 +817,49 @@ var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
|
|
|
793
817
|
// apps/cli/src/application/services/GitService.ts
|
|
794
818
|
var import_child_process = require("child_process");
|
|
795
819
|
var import_util = require("util");
|
|
820
|
+
var path = __toESM(require("path"));
|
|
796
821
|
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
797
822
|
var origin = "GitService";
|
|
798
823
|
var GitService = class {
|
|
799
824
|
constructor(logger2 = new PackmindLogger(origin)) {
|
|
800
825
|
this.logger = logger2;
|
|
801
826
|
}
|
|
802
|
-
async getGitRepositoryRoot(
|
|
827
|
+
async getGitRepositoryRoot(path8) {
|
|
803
828
|
try {
|
|
804
829
|
const { stdout } = await execAsync("git rev-parse --show-toplevel", {
|
|
805
|
-
cwd:
|
|
830
|
+
cwd: path8
|
|
806
831
|
});
|
|
807
832
|
const gitRoot = stdout.trim();
|
|
808
833
|
this.logger.debug("Resolved git repository root", {
|
|
809
|
-
inputPath:
|
|
834
|
+
inputPath: path8,
|
|
810
835
|
gitRoot
|
|
811
836
|
});
|
|
812
837
|
return gitRoot;
|
|
813
838
|
} catch (error) {
|
|
814
839
|
if (error instanceof Error) {
|
|
815
840
|
throw new Error(
|
|
816
|
-
`Failed to get Git repository root. The path '${
|
|
841
|
+
`Failed to get Git repository root. The path '${path8}' does not appear to be inside a Git repository.
|
|
817
842
|
${error.message}`
|
|
818
843
|
);
|
|
819
844
|
}
|
|
820
845
|
throw new Error("Failed to get Git repository root: Unknown error");
|
|
821
846
|
}
|
|
822
847
|
}
|
|
823
|
-
async tryGetGitRepositoryRoot(
|
|
848
|
+
async tryGetGitRepositoryRoot(path8) {
|
|
824
849
|
try {
|
|
825
|
-
return await this.getGitRepositoryRoot(
|
|
850
|
+
return await this.getGitRepositoryRoot(path8);
|
|
851
|
+
} catch {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
getGitRepositoryRootSync(cwd) {
|
|
856
|
+
try {
|
|
857
|
+
const result = (0, import_child_process.execSync)("git rev-parse --show-toplevel", {
|
|
858
|
+
cwd,
|
|
859
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
860
|
+
encoding: "utf-8"
|
|
861
|
+
});
|
|
862
|
+
return result.trim();
|
|
826
863
|
} catch {
|
|
827
864
|
return null;
|
|
828
865
|
}
|
|
@@ -927,18 +964,181 @@ ${error.message}`
|
|
|
927
964
|
normalizeGitUrl(url) {
|
|
928
965
|
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
929
966
|
if (sshMatch) {
|
|
930
|
-
const [, host,
|
|
931
|
-
const cleanPath =
|
|
967
|
+
const [, host, urlPath] = sshMatch;
|
|
968
|
+
const cleanPath = urlPath.replace(/\.git$/, "");
|
|
932
969
|
return `${host}/${cleanPath}`;
|
|
933
970
|
}
|
|
934
971
|
const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
935
972
|
if (httpsMatch) {
|
|
936
|
-
const [, host,
|
|
937
|
-
const cleanPath =
|
|
973
|
+
const [, host, urlPath] = httpsMatch;
|
|
974
|
+
const cleanPath = urlPath.replace(/\.git$/, "");
|
|
938
975
|
return `${host}/${cleanPath}`;
|
|
939
976
|
}
|
|
940
977
|
return url;
|
|
941
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Gets files that have been modified (staged + unstaged) compared to HEAD.
|
|
981
|
+
* Returns absolute file paths.
|
|
982
|
+
*/
|
|
983
|
+
async getModifiedFiles(repoPath) {
|
|
984
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
985
|
+
const trackedFiles = await this.getTrackedModifiedFiles(gitRoot);
|
|
986
|
+
const untrackedFiles = await this.getUntrackedFiles(gitRoot);
|
|
987
|
+
const allFiles = [.../* @__PURE__ */ new Set([...trackedFiles, ...untrackedFiles])];
|
|
988
|
+
this.logger.debug("Found modified files", {
|
|
989
|
+
trackedCount: trackedFiles.length,
|
|
990
|
+
untrackedCount: untrackedFiles.length,
|
|
991
|
+
totalCount: allFiles.length
|
|
992
|
+
});
|
|
993
|
+
return allFiles;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Gets tracked files that have been modified (staged + unstaged) compared to HEAD.
|
|
997
|
+
* Returns absolute file paths.
|
|
998
|
+
*/
|
|
999
|
+
async getTrackedModifiedFiles(gitRoot) {
|
|
1000
|
+
try {
|
|
1001
|
+
const { stdout } = await execAsync("git diff --name-only HEAD", {
|
|
1002
|
+
cwd: gitRoot
|
|
1003
|
+
});
|
|
1004
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
if (error instanceof Error && error.message.includes("unknown revision")) {
|
|
1007
|
+
this.logger.debug(
|
|
1008
|
+
"HEAD does not exist (first commit), getting staged files only"
|
|
1009
|
+
);
|
|
1010
|
+
return this.getStagedFilesWithoutHead(gitRoot);
|
|
1011
|
+
}
|
|
1012
|
+
throw error;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Gets staged files when HEAD doesn't exist (first commit scenario).
|
|
1017
|
+
*/
|
|
1018
|
+
async getStagedFilesWithoutHead(gitRoot) {
|
|
1019
|
+
const { stdout } = await execAsync("git diff --cached --name-only", {
|
|
1020
|
+
cwd: gitRoot
|
|
1021
|
+
});
|
|
1022
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Gets untracked files (new files not yet added to git).
|
|
1026
|
+
* Returns absolute file paths.
|
|
1027
|
+
*/
|
|
1028
|
+
async getUntrackedFiles(repoPath) {
|
|
1029
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
1030
|
+
const { stdout } = await execAsync(
|
|
1031
|
+
"git ls-files --others --exclude-standard",
|
|
1032
|
+
{
|
|
1033
|
+
cwd: gitRoot
|
|
1034
|
+
}
|
|
1035
|
+
);
|
|
1036
|
+
return stdout.trim().split("\n").filter((line) => line.length > 0).map((relativePath) => path.join(gitRoot, relativePath));
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Gets line-level diff information for modified files.
|
|
1040
|
+
* For untracked files, all lines are considered modified (new file).
|
|
1041
|
+
* Returns ModifiedLine objects with absolute file paths.
|
|
1042
|
+
*/
|
|
1043
|
+
async getModifiedLines(repoPath) {
|
|
1044
|
+
const gitRoot = await this.getGitRepositoryRoot(repoPath);
|
|
1045
|
+
const modifiedLines = [];
|
|
1046
|
+
const trackedModifications = await this.getTrackedModifiedLines(gitRoot);
|
|
1047
|
+
modifiedLines.push(...trackedModifications);
|
|
1048
|
+
const untrackedFiles = await this.getUntrackedFiles(gitRoot);
|
|
1049
|
+
for (const filePath of untrackedFiles) {
|
|
1050
|
+
const lineCount = await this.countFileLines(filePath);
|
|
1051
|
+
if (lineCount > 0) {
|
|
1052
|
+
modifiedLines.push({
|
|
1053
|
+
file: filePath,
|
|
1054
|
+
startLine: 1,
|
|
1055
|
+
lineCount
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
this.logger.debug("Found modified lines", {
|
|
1060
|
+
trackedEntries: trackedModifications.length,
|
|
1061
|
+
untrackedFiles: untrackedFiles.length,
|
|
1062
|
+
totalEntries: modifiedLines.length
|
|
1063
|
+
});
|
|
1064
|
+
return modifiedLines;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Parses git diff output to extract line-level modifications.
|
|
1068
|
+
*/
|
|
1069
|
+
async getTrackedModifiedLines(gitRoot) {
|
|
1070
|
+
try {
|
|
1071
|
+
const { stdout } = await execAsync("git diff HEAD --unified=0", {
|
|
1072
|
+
cwd: gitRoot,
|
|
1073
|
+
maxBuffer: 50 * 1024 * 1024
|
|
1074
|
+
// 50MB buffer for large diffs
|
|
1075
|
+
});
|
|
1076
|
+
return this.parseDiffOutput(stdout, gitRoot);
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
if (error instanceof Error && error.message.includes("unknown revision")) {
|
|
1079
|
+
this.logger.debug(
|
|
1080
|
+
"HEAD does not exist (first commit), getting staged diff only"
|
|
1081
|
+
);
|
|
1082
|
+
return this.getStagedModifiedLinesWithoutHead(gitRoot);
|
|
1083
|
+
}
|
|
1084
|
+
throw error;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Gets modified lines from staged files when HEAD doesn't exist.
|
|
1089
|
+
*/
|
|
1090
|
+
async getStagedModifiedLinesWithoutHead(gitRoot) {
|
|
1091
|
+
const { stdout } = await execAsync("git diff --cached --unified=0", {
|
|
1092
|
+
cwd: gitRoot,
|
|
1093
|
+
maxBuffer: 50 * 1024 * 1024
|
|
1094
|
+
});
|
|
1095
|
+
return this.parseDiffOutput(stdout, gitRoot);
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Parses unified diff output to extract modified line ranges.
|
|
1099
|
+
* Format: @@ -oldStart,oldCount +newStart,newCount @@
|
|
1100
|
+
*/
|
|
1101
|
+
parseDiffOutput(diffOutput, gitRoot) {
|
|
1102
|
+
const modifiedLines = [];
|
|
1103
|
+
const lines = diffOutput.split("\n");
|
|
1104
|
+
let currentFile = null;
|
|
1105
|
+
for (const line of lines) {
|
|
1106
|
+
const fileMatch = line.match(/^diff --git a\/(.+) b\/(.+)$/);
|
|
1107
|
+
if (fileMatch) {
|
|
1108
|
+
currentFile = path.join(gitRoot, fileMatch[2]);
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const hunkMatch = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
|
|
1112
|
+
if (hunkMatch && currentFile) {
|
|
1113
|
+
const startLine = parseInt(hunkMatch[1], 10);
|
|
1114
|
+
const lineCount = hunkMatch[2] ? parseInt(hunkMatch[2], 10) : 1;
|
|
1115
|
+
if (lineCount > 0) {
|
|
1116
|
+
modifiedLines.push({
|
|
1117
|
+
file: currentFile,
|
|
1118
|
+
startLine,
|
|
1119
|
+
lineCount
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return modifiedLines;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Counts the number of lines in a file.
|
|
1128
|
+
*/
|
|
1129
|
+
async countFileLines(filePath) {
|
|
1130
|
+
try {
|
|
1131
|
+
const { stdout } = await execAsync(`wc -l < "${filePath}"`);
|
|
1132
|
+
const count = parseInt(stdout.trim(), 10);
|
|
1133
|
+
if (count === 0) {
|
|
1134
|
+
const { stdout: content } = await execAsync(`head -c 1 "${filePath}"`);
|
|
1135
|
+
return content.length > 0 ? 1 : 0;
|
|
1136
|
+
}
|
|
1137
|
+
return count;
|
|
1138
|
+
} catch {
|
|
1139
|
+
return 0;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
942
1142
|
};
|
|
943
1143
|
|
|
944
1144
|
// apps/cli/src/application/useCases/GetGitRemoteUrlUseCase.ts
|
|
@@ -954,7 +1154,7 @@ var GetGitRemoteUrlUseCase = class {
|
|
|
954
1154
|
|
|
955
1155
|
// apps/cli/src/application/services/ListFiles.ts
|
|
956
1156
|
var fs = __toESM(require("fs/promises"));
|
|
957
|
-
var
|
|
1157
|
+
var path2 = __toESM(require("path"));
|
|
958
1158
|
var ListFiles = class {
|
|
959
1159
|
async listFilesInDirectory(directoryPath, extensions, excludes = [], skipHidden = true) {
|
|
960
1160
|
const results = [];
|
|
@@ -976,7 +1176,7 @@ var ListFiles = class {
|
|
|
976
1176
|
try {
|
|
977
1177
|
const entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
978
1178
|
for (const entry of entries) {
|
|
979
|
-
const fullPath =
|
|
1179
|
+
const fullPath = path2.join(directoryPath, entry.name);
|
|
980
1180
|
if (this.shouldExcludePath(fullPath, excludes)) {
|
|
981
1181
|
continue;
|
|
982
1182
|
}
|
|
@@ -993,7 +1193,7 @@ var ListFiles = class {
|
|
|
993
1193
|
skipHidden
|
|
994
1194
|
);
|
|
995
1195
|
} else if (entry.isFile()) {
|
|
996
|
-
const fileExtension =
|
|
1196
|
+
const fileExtension = path2.extname(entry.name);
|
|
997
1197
|
if (includeAllExtensions || extensions.includes(fileExtension)) {
|
|
998
1198
|
results.push({
|
|
999
1199
|
path: fullPath
|
|
@@ -1009,7 +1209,7 @@ var ListFiles = class {
|
|
|
1009
1209
|
if (excludes.length === 0) {
|
|
1010
1210
|
return false;
|
|
1011
1211
|
}
|
|
1012
|
-
const normalizedPath =
|
|
1212
|
+
const normalizedPath = path2.normalize(filePath).replace(/\\/g, "/");
|
|
1013
1213
|
for (const exclude of excludes) {
|
|
1014
1214
|
if (this.matchesGlobPattern(normalizedPath, exclude)) {
|
|
1015
1215
|
return true;
|
|
@@ -1072,7 +1272,7 @@ var ListFilesInDirectoryUseCase = class {
|
|
|
1072
1272
|
|
|
1073
1273
|
// apps/cli/src/application/useCases/LintFilesInDirectoryUseCase.ts
|
|
1074
1274
|
var import_minimatch = require("minimatch");
|
|
1075
|
-
var
|
|
1275
|
+
var path3 = __toESM(require("path"));
|
|
1076
1276
|
var fs2 = __toESM(require("fs/promises"));
|
|
1077
1277
|
var origin2 = "LintFilesInDirectoryUseCase";
|
|
1078
1278
|
var LintFilesInDirectoryUseCase = class {
|
|
@@ -1139,12 +1339,13 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1139
1339
|
draftMode,
|
|
1140
1340
|
standardSlug,
|
|
1141
1341
|
ruleId,
|
|
1142
|
-
language
|
|
1342
|
+
language,
|
|
1343
|
+
diffMode
|
|
1143
1344
|
} = command3;
|
|
1144
1345
|
this.logger.debug(
|
|
1145
|
-
`Starting linting: path="${userPath}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}"`
|
|
1346
|
+
`Starting linting: path="${userPath}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}", diffMode="${diffMode ?? "none"}"`
|
|
1146
1347
|
);
|
|
1147
|
-
const absoluteUserPath =
|
|
1348
|
+
const absoluteUserPath = path3.isAbsolute(userPath) ? userPath : path3.resolve(process.cwd(), userPath);
|
|
1148
1349
|
let pathStats;
|
|
1149
1350
|
try {
|
|
1150
1351
|
pathStats = await fs2.stat(absoluteUserPath);
|
|
@@ -1154,7 +1355,7 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1154
1355
|
);
|
|
1155
1356
|
}
|
|
1156
1357
|
const isFile = pathStats.isFile();
|
|
1157
|
-
const directoryForGitOps = isFile ?
|
|
1358
|
+
const directoryForGitOps = isFile ? path3.dirname(absoluteUserPath) : absoluteUserPath;
|
|
1158
1359
|
this.logger.debug(
|
|
1159
1360
|
`Path type: ${isFile ? "file" : "directory"}, gitOpsDir="${directoryForGitOps}"`
|
|
1160
1361
|
);
|
|
@@ -1170,11 +1371,60 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1170
1371
|
this.logger.debug(
|
|
1171
1372
|
`Resolved paths: gitRoot="${gitRepoRoot}", lintPath="${absoluteLintPath}"`
|
|
1172
1373
|
);
|
|
1173
|
-
|
|
1374
|
+
let modifiedFiles = null;
|
|
1375
|
+
let modifiedLines = null;
|
|
1376
|
+
if (diffMode) {
|
|
1377
|
+
if (diffMode === "files" /* FILES */) {
|
|
1378
|
+
modifiedFiles = await this.services.gitRemoteUrlService.getModifiedFiles(gitRepoRoot);
|
|
1379
|
+
this.logger.debug(`Found ${modifiedFiles.length} modified files`);
|
|
1380
|
+
if (modifiedFiles.length === 0) {
|
|
1381
|
+
const { gitRemoteUrl: gitRemoteUrl2 } = await this.services.gitRemoteUrlService.getGitRemoteUrl(
|
|
1382
|
+
gitRepoRoot
|
|
1383
|
+
);
|
|
1384
|
+
return {
|
|
1385
|
+
gitRemoteUrl: gitRemoteUrl2,
|
|
1386
|
+
violations: [],
|
|
1387
|
+
summary: {
|
|
1388
|
+
totalFiles: 0,
|
|
1389
|
+
violatedFiles: 0,
|
|
1390
|
+
totalViolations: 0,
|
|
1391
|
+
standardsChecked: []
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
} else if (diffMode === "lines" /* LINES */) {
|
|
1396
|
+
modifiedLines = await this.services.gitRemoteUrlService.getModifiedLines(gitRepoRoot);
|
|
1397
|
+
modifiedFiles = [...new Set(modifiedLines.map((ml) => ml.file))];
|
|
1398
|
+
this.logger.debug(
|
|
1399
|
+
`Found ${modifiedLines.length} modified line ranges in ${modifiedFiles.length} files`
|
|
1400
|
+
);
|
|
1401
|
+
if (modifiedFiles.length === 0) {
|
|
1402
|
+
const { gitRemoteUrl: gitRemoteUrl2 } = await this.services.gitRemoteUrlService.getGitRemoteUrl(
|
|
1403
|
+
gitRepoRoot
|
|
1404
|
+
);
|
|
1405
|
+
return {
|
|
1406
|
+
gitRemoteUrl: gitRemoteUrl2,
|
|
1407
|
+
violations: [],
|
|
1408
|
+
summary: {
|
|
1409
|
+
totalFiles: 0,
|
|
1410
|
+
violatedFiles: 0,
|
|
1411
|
+
totalViolations: 0,
|
|
1412
|
+
standardsChecked: []
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
let files = isFile ? [{ path: absoluteLintPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
1174
1419
|
absoluteLintPath,
|
|
1175
1420
|
[],
|
|
1176
1421
|
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
1177
1422
|
);
|
|
1423
|
+
if (modifiedFiles) {
|
|
1424
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
1425
|
+
files = files.filter((file) => modifiedFilesSet.has(file.path));
|
|
1426
|
+
this.logger.debug(`Filtered to ${files.length} modified files`);
|
|
1427
|
+
}
|
|
1178
1428
|
const { gitRemoteUrl } = await this.services.gitRemoteUrlService.getGitRemoteUrl(gitRepoRoot);
|
|
1179
1429
|
const { branches } = await this.services.gitRemoteUrlService.getCurrentBranches(gitRepoRoot);
|
|
1180
1430
|
this.logger.debug(
|
|
@@ -1371,7 +1621,17 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1371
1621
|
});
|
|
1372
1622
|
}
|
|
1373
1623
|
}
|
|
1374
|
-
|
|
1624
|
+
let filteredViolations = violations;
|
|
1625
|
+
if (diffMode === "lines" /* LINES */ && modifiedLines) {
|
|
1626
|
+
filteredViolations = this.services.diffViolationFilterService.filterByLines(
|
|
1627
|
+
violations,
|
|
1628
|
+
modifiedLines
|
|
1629
|
+
);
|
|
1630
|
+
this.logger.debug(
|
|
1631
|
+
`Filtered violations by lines: ${violations.length} -> ${filteredViolations.length}`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const totalViolations = filteredViolations.reduce(
|
|
1375
1635
|
(sum, violation) => sum + violation.violations.length,
|
|
1376
1636
|
0
|
|
1377
1637
|
);
|
|
@@ -1384,10 +1644,10 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1384
1644
|
);
|
|
1385
1645
|
return {
|
|
1386
1646
|
gitRemoteUrl,
|
|
1387
|
-
violations,
|
|
1647
|
+
violations: filteredViolations,
|
|
1388
1648
|
summary: {
|
|
1389
1649
|
totalFiles: files.length,
|
|
1390
|
-
violatedFiles:
|
|
1650
|
+
violatedFiles: filteredViolations.length,
|
|
1391
1651
|
totalViolations,
|
|
1392
1652
|
standardsChecked
|
|
1393
1653
|
}
|
|
@@ -1415,7 +1675,7 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1415
1675
|
|
|
1416
1676
|
// apps/cli/src/application/useCases/LintFilesLocallyUseCase.ts
|
|
1417
1677
|
var import_minimatch2 = require("minimatch");
|
|
1418
|
-
var
|
|
1678
|
+
var path4 = __toESM(require("path"));
|
|
1419
1679
|
var fs3 = __toESM(require("fs/promises"));
|
|
1420
1680
|
var origin3 = "LintFilesLocallyUseCase";
|
|
1421
1681
|
var LintFilesLocallyUseCase = class {
|
|
@@ -1423,6 +1683,7 @@ var LintFilesLocallyUseCase = class {
|
|
|
1423
1683
|
this.services = services;
|
|
1424
1684
|
this.repositories = repositories;
|
|
1425
1685
|
this.logger = logger2;
|
|
1686
|
+
this.detectionProgramsCache = /* @__PURE__ */ new Map();
|
|
1426
1687
|
}
|
|
1427
1688
|
fileMatchesTargetAndScope(filePath, targetPath, scopePatterns) {
|
|
1428
1689
|
if (!scopePatterns || scopePatterns.length === 0) {
|
|
@@ -1458,9 +1719,12 @@ var LintFilesLocallyUseCase = class {
|
|
|
1458
1719
|
return pattern;
|
|
1459
1720
|
}
|
|
1460
1721
|
async execute(command3) {
|
|
1461
|
-
const { path: userPath } = command3;
|
|
1462
|
-
this.logger.debug(
|
|
1463
|
-
|
|
1722
|
+
const { path: userPath, diffMode } = command3;
|
|
1723
|
+
this.logger.debug(
|
|
1724
|
+
`Starting local linting: path="${userPath}", diffMode="${diffMode ?? "none"}"`
|
|
1725
|
+
);
|
|
1726
|
+
this.detectionProgramsCache.clear();
|
|
1727
|
+
const absoluteUserPath = path4.isAbsolute(userPath) ? userPath : path4.resolve(process.cwd(), userPath);
|
|
1464
1728
|
let pathStats;
|
|
1465
1729
|
try {
|
|
1466
1730
|
pathStats = await fs3.stat(absoluteUserPath);
|
|
@@ -1470,44 +1734,78 @@ var LintFilesLocallyUseCase = class {
|
|
|
1470
1734
|
);
|
|
1471
1735
|
}
|
|
1472
1736
|
const isFile = pathStats.isFile();
|
|
1473
|
-
const directoryForConfig = isFile ?
|
|
1737
|
+
const directoryForConfig = isFile ? path4.dirname(absoluteUserPath) : absoluteUserPath;
|
|
1474
1738
|
const gitRepoRoot = await this.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
|
|
1475
1739
|
directoryForConfig
|
|
1476
1740
|
);
|
|
1477
|
-
|
|
1741
|
+
let modifiedFiles = null;
|
|
1742
|
+
let modifiedLines = null;
|
|
1743
|
+
if (diffMode && gitRepoRoot) {
|
|
1744
|
+
if (diffMode === "files" /* FILES */) {
|
|
1745
|
+
modifiedFiles = await this.services.gitRemoteUrlService.getModifiedFiles(gitRepoRoot);
|
|
1746
|
+
this.logger.debug(`Found ${modifiedFiles.length} modified files`);
|
|
1747
|
+
if (modifiedFiles.length === 0) {
|
|
1748
|
+
return {
|
|
1749
|
+
violations: [],
|
|
1750
|
+
summary: {
|
|
1751
|
+
totalFiles: 0,
|
|
1752
|
+
violatedFiles: 0,
|
|
1753
|
+
totalViolations: 0,
|
|
1754
|
+
standardsChecked: []
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
} else if (diffMode === "lines" /* LINES */) {
|
|
1759
|
+
modifiedLines = await this.services.gitRemoteUrlService.getModifiedLines(gitRepoRoot);
|
|
1760
|
+
modifiedFiles = [...new Set(modifiedLines.map((ml) => ml.file))];
|
|
1761
|
+
this.logger.debug(
|
|
1762
|
+
`Found ${modifiedLines.length} modified line ranges in ${modifiedFiles.length} files`
|
|
1763
|
+
);
|
|
1764
|
+
if (modifiedFiles.length === 0) {
|
|
1765
|
+
return {
|
|
1766
|
+
violations: [],
|
|
1767
|
+
summary: {
|
|
1768
|
+
totalFiles: 0,
|
|
1769
|
+
violatedFiles: 0,
|
|
1770
|
+
totalViolations: 0,
|
|
1771
|
+
standardsChecked: []
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const allConfigs = await this.repositories.configFileRepository.findAllConfigsInTree(
|
|
1478
1778
|
directoryForConfig,
|
|
1479
1779
|
gitRepoRoot
|
|
1480
1780
|
);
|
|
1481
|
-
if (!
|
|
1781
|
+
if (!allConfigs.hasConfigs) {
|
|
1482
1782
|
const boundary = gitRepoRoot ?? "filesystem root";
|
|
1483
1783
|
throw new Error(
|
|
1484
1784
|
`No packmind.json found between ${directoryForConfig} and ${boundary}. Cannot use local linting.`
|
|
1485
1785
|
);
|
|
1486
1786
|
}
|
|
1487
|
-
const basePath =
|
|
1787
|
+
const basePath = allConfigs.basePath;
|
|
1488
1788
|
this.logger.debug(
|
|
1489
|
-
`Found ${
|
|
1789
|
+
`Found ${allConfigs.configs.length} packmind.json file(s)`
|
|
1490
1790
|
);
|
|
1491
|
-
for (const
|
|
1492
|
-
this.logger.debug(
|
|
1791
|
+
for (const config of allConfigs.configs) {
|
|
1792
|
+
this.logger.debug(
|
|
1793
|
+
`Using config: ${config.absoluteTargetPath}/packmind.json (target: ${config.targetPath})`
|
|
1794
|
+
);
|
|
1493
1795
|
}
|
|
1494
|
-
|
|
1495
|
-
this.logger.debug(
|
|
1496
|
-
`Merged ${packageSlugs.length} packages from configuration files`
|
|
1497
|
-
);
|
|
1498
|
-
const detectionPrograms = await this.repositories.packmindGateway.getDetectionProgramsForPackages({
|
|
1499
|
-
packagesSlugs: packageSlugs
|
|
1500
|
-
});
|
|
1501
|
-
this.logger.debug(
|
|
1502
|
-
`Retrieved detection programs: targetsCount=${detectionPrograms.targets.length}`
|
|
1503
|
-
);
|
|
1504
|
-
const files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
1796
|
+
let files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
1505
1797
|
absoluteUserPath,
|
|
1506
1798
|
[],
|
|
1507
1799
|
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
1508
1800
|
);
|
|
1801
|
+
if (modifiedFiles) {
|
|
1802
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
1803
|
+
files = files.filter((file) => modifiedFilesSet.has(file.path));
|
|
1804
|
+
this.logger.debug(`Filtered to ${files.length} modified files`);
|
|
1805
|
+
}
|
|
1509
1806
|
this.logger.debug(`Found ${files.length} files to lint`);
|
|
1510
1807
|
const violations = [];
|
|
1808
|
+
const allStandardsChecked = /* @__PURE__ */ new Set();
|
|
1511
1809
|
for (const file of files) {
|
|
1512
1810
|
const fileViolations = [];
|
|
1513
1811
|
const relativeFilePath = file.path.startsWith(basePath) ? file.path.substring(basePath.length) : file.path;
|
|
@@ -1520,38 +1818,46 @@ var LintFilesLocallyUseCase = class {
|
|
|
1520
1818
|
if (!fileLanguage) {
|
|
1521
1819
|
continue;
|
|
1522
1820
|
}
|
|
1821
|
+
const matchingTargets = this.findMatchingTargets(
|
|
1822
|
+
file.path,
|
|
1823
|
+
allConfigs.configs
|
|
1824
|
+
);
|
|
1523
1825
|
const programsByLanguage = /* @__PURE__ */ new Map();
|
|
1524
|
-
for (const
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1826
|
+
for (const targetConfig of matchingTargets) {
|
|
1827
|
+
const detectionPrograms = await this.getDetectionProgramsForTarget(targetConfig);
|
|
1828
|
+
for (const target of detectionPrograms.targets) {
|
|
1829
|
+
for (const standard of target.standards) {
|
|
1830
|
+
if (!this.fileMatchesTargetAndScope(
|
|
1831
|
+
normalizedFilePath,
|
|
1832
|
+
targetConfig.targetPath,
|
|
1833
|
+
standard.scope
|
|
1834
|
+
)) {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
allStandardsChecked.add(standard.slug);
|
|
1838
|
+
for (const rule of standard.rules) {
|
|
1839
|
+
for (const activeProgram of rule.activeDetectionPrograms) {
|
|
1840
|
+
try {
|
|
1841
|
+
const programLanguage = this.resolveProgrammingLanguage(
|
|
1842
|
+
activeProgram.language
|
|
1843
|
+
);
|
|
1844
|
+
if (!programLanguage || programLanguage !== fileLanguage) {
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
|
|
1848
|
+
programsForLanguage.push({
|
|
1849
|
+
code: activeProgram.detectionProgram.code,
|
|
1850
|
+
ruleContent: rule.content,
|
|
1851
|
+
standardSlug: standard.slug,
|
|
1852
|
+
sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
|
|
1853
|
+
language: fileLanguage
|
|
1854
|
+
});
|
|
1855
|
+
programsByLanguage.set(programLanguage, programsForLanguage);
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
console.error(
|
|
1858
|
+
`Error preparing program for file ${file.path}: ${error}`
|
|
1859
|
+
);
|
|
1541
1860
|
}
|
|
1542
|
-
const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
|
|
1543
|
-
programsForLanguage.push({
|
|
1544
|
-
code: activeProgram.detectionProgram.code,
|
|
1545
|
-
ruleContent: rule.content,
|
|
1546
|
-
standardSlug: standard.slug,
|
|
1547
|
-
sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
|
|
1548
|
-
language: fileLanguage
|
|
1549
|
-
});
|
|
1550
|
-
programsByLanguage.set(programLanguage, programsForLanguage);
|
|
1551
|
-
} catch (error) {
|
|
1552
|
-
console.error(
|
|
1553
|
-
`Error preparing program for file ${file.path}: ${error}`
|
|
1554
|
-
);
|
|
1555
1861
|
}
|
|
1556
1862
|
}
|
|
1557
1863
|
}
|
|
@@ -1590,27 +1896,64 @@ var LintFilesLocallyUseCase = class {
|
|
|
1590
1896
|
});
|
|
1591
1897
|
}
|
|
1592
1898
|
}
|
|
1593
|
-
|
|
1899
|
+
let filteredViolations = violations;
|
|
1900
|
+
if (diffMode === "lines" /* LINES */ && modifiedLines) {
|
|
1901
|
+
filteredViolations = this.services.diffViolationFilterService.filterByLines(
|
|
1902
|
+
violations,
|
|
1903
|
+
modifiedLines
|
|
1904
|
+
);
|
|
1905
|
+
this.logger.debug(
|
|
1906
|
+
`Filtered violations by lines: ${violations.length} -> ${filteredViolations.length}`
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
const totalViolations = filteredViolations.reduce(
|
|
1594
1910
|
(sum, violation) => sum + violation.violations.length,
|
|
1595
1911
|
0
|
|
1596
1912
|
);
|
|
1597
|
-
const standardsChecked = Array.from(
|
|
1598
|
-
new Set(
|
|
1599
|
-
detectionPrograms.targets.flatMap(
|
|
1600
|
-
(target) => target.standards.map((standard) => standard.slug)
|
|
1601
|
-
)
|
|
1602
|
-
)
|
|
1603
|
-
);
|
|
1604
1913
|
return {
|
|
1605
|
-
violations,
|
|
1914
|
+
violations: filteredViolations,
|
|
1606
1915
|
summary: {
|
|
1607
1916
|
totalFiles: files.length,
|
|
1608
|
-
violatedFiles:
|
|
1917
|
+
violatedFiles: filteredViolations.length,
|
|
1609
1918
|
totalViolations,
|
|
1610
|
-
standardsChecked
|
|
1919
|
+
standardsChecked: Array.from(allStandardsChecked)
|
|
1611
1920
|
}
|
|
1612
1921
|
};
|
|
1613
1922
|
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Finds all targets (configs) that are ancestors of the given file path.
|
|
1925
|
+
* A target matches if the file is located within or under the target's directory.
|
|
1926
|
+
*/
|
|
1927
|
+
findMatchingTargets(absoluteFilePath, configs) {
|
|
1928
|
+
return configs.filter(
|
|
1929
|
+
(config) => absoluteFilePath.startsWith(config.absoluteTargetPath + "/") || absoluteFilePath === config.absoluteTargetPath
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Gets detection programs for a target, using cache to avoid redundant API calls.
|
|
1934
|
+
* Cache key is the sorted package slugs to handle identical package sets.
|
|
1935
|
+
*/
|
|
1936
|
+
async getDetectionProgramsForTarget(targetConfig) {
|
|
1937
|
+
const packageSlugs = Object.keys(targetConfig.packages).sort(
|
|
1938
|
+
(a, b) => a.localeCompare(b)
|
|
1939
|
+
);
|
|
1940
|
+
const cacheKey = packageSlugs.join(",");
|
|
1941
|
+
const cached = this.detectionProgramsCache.get(cacheKey);
|
|
1942
|
+
if (cached) {
|
|
1943
|
+
this.logger.debug(
|
|
1944
|
+
`Using cached detection programs for packages: ${cacheKey}`
|
|
1945
|
+
);
|
|
1946
|
+
return cached;
|
|
1947
|
+
}
|
|
1948
|
+
this.logger.debug(
|
|
1949
|
+
`Fetching detection programs for packages: ${packageSlugs.join(", ")}`
|
|
1950
|
+
);
|
|
1951
|
+
const detectionPrograms = await this.repositories.packmindGateway.getDetectionProgramsForPackages({
|
|
1952
|
+
packagesSlugs: packageSlugs
|
|
1953
|
+
});
|
|
1954
|
+
this.detectionProgramsCache.set(cacheKey, detectionPrograms);
|
|
1955
|
+
return detectionPrograms;
|
|
1956
|
+
}
|
|
1614
1957
|
resolveProgrammingLanguage(language) {
|
|
1615
1958
|
try {
|
|
1616
1959
|
return stringToProgrammingLanguage(language);
|
|
@@ -2065,6 +2408,71 @@ var PackmindGateway = class {
|
|
|
2065
2408
|
}
|
|
2066
2409
|
};
|
|
2067
2410
|
|
|
2411
|
+
// apps/cli/src/application/services/DiffViolationFilterService.ts
|
|
2412
|
+
var DiffViolationFilterService = class {
|
|
2413
|
+
/**
|
|
2414
|
+
* Filters violations to only include those in modified files.
|
|
2415
|
+
* @param violations - The list of violations to filter
|
|
2416
|
+
* @param modifiedFiles - The list of absolute paths of modified files
|
|
2417
|
+
* @returns Violations that occur in modified files
|
|
2418
|
+
*/
|
|
2419
|
+
filterByFiles(violations, modifiedFiles) {
|
|
2420
|
+
const modifiedFilesSet = new Set(modifiedFiles);
|
|
2421
|
+
return violations.filter(
|
|
2422
|
+
(violation) => modifiedFilesSet.has(violation.file)
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Filters violations to only include those on modified lines.
|
|
2427
|
+
* @param violations - The list of violations to filter
|
|
2428
|
+
* @param modifiedLines - The list of modified line ranges
|
|
2429
|
+
* @returns Violations that occur on modified lines
|
|
2430
|
+
*/
|
|
2431
|
+
filterByLines(violations, modifiedLines) {
|
|
2432
|
+
const modifiedLinesByFile = this.groupModifiedLinesByFile(modifiedLines);
|
|
2433
|
+
return violations.map((violation) => {
|
|
2434
|
+
const fileModifications = modifiedLinesByFile.get(violation.file);
|
|
2435
|
+
if (!fileModifications) {
|
|
2436
|
+
return null;
|
|
2437
|
+
}
|
|
2438
|
+
const filteredViolations = violation.violations.filter(
|
|
2439
|
+
(singleViolation) => this.isLineInModifiedRanges(
|
|
2440
|
+
singleViolation.line,
|
|
2441
|
+
fileModifications
|
|
2442
|
+
)
|
|
2443
|
+
);
|
|
2444
|
+
if (filteredViolations.length === 0) {
|
|
2445
|
+
return null;
|
|
2446
|
+
}
|
|
2447
|
+
return {
|
|
2448
|
+
file: violation.file,
|
|
2449
|
+
violations: filteredViolations
|
|
2450
|
+
};
|
|
2451
|
+
}).filter((v) => v !== null);
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Groups modified lines by file path for efficient lookup.
|
|
2455
|
+
*/
|
|
2456
|
+
groupModifiedLinesByFile(modifiedLines) {
|
|
2457
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
2458
|
+
for (const modification of modifiedLines) {
|
|
2459
|
+
const existing = byFile.get(modification.file) ?? [];
|
|
2460
|
+
existing.push(modification);
|
|
2461
|
+
byFile.set(modification.file, existing);
|
|
2462
|
+
}
|
|
2463
|
+
return byFile;
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Checks if a line number falls within any of the modified line ranges.
|
|
2467
|
+
*/
|
|
2468
|
+
isLineInModifiedRanges(line, modifications) {
|
|
2469
|
+
return modifications.some((mod) => {
|
|
2470
|
+
const endLine = mod.startLine + mod.lineCount - 1;
|
|
2471
|
+
return line >= mod.startLine && line <= endLine;
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
|
|
2068
2476
|
// packages/linter-ast/src/core/ParserError.ts
|
|
2069
2477
|
var ParserNotAvailableError = class extends Error {
|
|
2070
2478
|
constructor(language, cause) {
|
|
@@ -3867,9 +4275,9 @@ var ExecuteLinterProgramsUseCase = class {
|
|
|
3867
4275
|
let line;
|
|
3868
4276
|
let character = 0;
|
|
3869
4277
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3870
|
-
line = value;
|
|
4278
|
+
line = value + 1;
|
|
3871
4279
|
} else if (this.isViolationLike(value)) {
|
|
3872
|
-
line = value.line;
|
|
4280
|
+
line = value.line + 1;
|
|
3873
4281
|
character = value.character ?? 0;
|
|
3874
4282
|
}
|
|
3875
4283
|
if (!this.isValidLine(line)) {
|
|
@@ -4813,7 +5221,7 @@ ${sectionBlock}
|
|
|
4813
5221
|
|
|
4814
5222
|
// apps/cli/src/application/useCases/PullDataUseCase.ts
|
|
4815
5223
|
var fs4 = __toESM(require("fs/promises"));
|
|
4816
|
-
var
|
|
5224
|
+
var path5 = __toESM(require("path"));
|
|
4817
5225
|
var PullDataUseCase = class {
|
|
4818
5226
|
constructor(packmindGateway) {
|
|
4819
5227
|
this.packmindGateway = packmindGateway;
|
|
@@ -4869,8 +5277,8 @@ var PullDataUseCase = class {
|
|
|
4869
5277
|
return result;
|
|
4870
5278
|
}
|
|
4871
5279
|
async createOrUpdateFile(baseDirectory, file, result) {
|
|
4872
|
-
const fullPath =
|
|
4873
|
-
const directory =
|
|
5280
|
+
const fullPath = path5.join(baseDirectory, file.path);
|
|
5281
|
+
const directory = path5.dirname(fullPath);
|
|
4874
5282
|
await fs4.mkdir(directory, { recursive: true });
|
|
4875
5283
|
const fileExists = await this.fileExists(fullPath);
|
|
4876
5284
|
if (file.content !== void 0) {
|
|
@@ -4927,7 +5335,7 @@ var PullDataUseCase = class {
|
|
|
4927
5335
|
}
|
|
4928
5336
|
}
|
|
4929
5337
|
async deleteFile(baseDirectory, filePath, result) {
|
|
4930
|
-
const fullPath =
|
|
5338
|
+
const fullPath = path5.join(baseDirectory, filePath);
|
|
4931
5339
|
const fileExists = await this.fileExists(fullPath);
|
|
4932
5340
|
if (fileExists) {
|
|
4933
5341
|
await fs4.unlink(fullPath);
|
|
@@ -5011,10 +5419,41 @@ var GetPackageSummaryUseCase = class {
|
|
|
5011
5419
|
|
|
5012
5420
|
// apps/cli/src/infra/repositories/ConfigFileRepository.ts
|
|
5013
5421
|
var fs5 = __toESM(require("fs/promises"));
|
|
5014
|
-
var
|
|
5422
|
+
var path6 = __toESM(require("path"));
|
|
5423
|
+
|
|
5424
|
+
// apps/cli/src/infra/utils/consoleLogger.ts
|
|
5425
|
+
var import_chalk = __toESM(require("chalk"));
|
|
5426
|
+
var CLI_PREFIX = "packmind-cli";
|
|
5427
|
+
function logWarningConsole(message) {
|
|
5428
|
+
console.warn(import_chalk.default.bgYellow.bold(CLI_PREFIX), import_chalk.default.yellow(message));
|
|
5429
|
+
}
|
|
5430
|
+
function logErrorConsole(message) {
|
|
5431
|
+
console.error(import_chalk.default.bgRed.bold(CLI_PREFIX), import_chalk.default.red(message));
|
|
5432
|
+
}
|
|
5433
|
+
function logSuccessConsole(message) {
|
|
5434
|
+
console.log(import_chalk.default.bgGreen.bold(CLI_PREFIX), import_chalk.default.green.bold(message));
|
|
5435
|
+
}
|
|
5436
|
+
function formatSlug(text) {
|
|
5437
|
+
return import_chalk.default.blue.bold(text);
|
|
5438
|
+
}
|
|
5439
|
+
function formatLabel(text) {
|
|
5440
|
+
return import_chalk.default.dim(text);
|
|
5441
|
+
}
|
|
5442
|
+
function formatError(text) {
|
|
5443
|
+
return import_chalk.default.red(text);
|
|
5444
|
+
}
|
|
5445
|
+
function formatBold(text) {
|
|
5446
|
+
return import_chalk.default.bold(text);
|
|
5447
|
+
}
|
|
5448
|
+
function formatFilePath(text) {
|
|
5449
|
+
return import_chalk.default.underline.gray(text);
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5452
|
+
// apps/cli/src/infra/repositories/ConfigFileRepository.ts
|
|
5015
5453
|
var ConfigFileRepository = class {
|
|
5016
5454
|
constructor() {
|
|
5017
5455
|
this.CONFIG_FILENAME = "packmind.json";
|
|
5456
|
+
this.warnedFiles = /* @__PURE__ */ new Set();
|
|
5018
5457
|
this.EXCLUDED_DIRECTORIES = [
|
|
5019
5458
|
"node_modules",
|
|
5020
5459
|
".git",
|
|
@@ -5025,12 +5464,12 @@ var ConfigFileRepository = class {
|
|
|
5025
5464
|
];
|
|
5026
5465
|
}
|
|
5027
5466
|
async writeConfig(baseDirectory, config) {
|
|
5028
|
-
const configPath =
|
|
5467
|
+
const configPath = path6.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5029
5468
|
const configContent = JSON.stringify(config, null, 2) + "\n";
|
|
5030
5469
|
await fs5.writeFile(configPath, configContent, "utf-8");
|
|
5031
5470
|
}
|
|
5032
5471
|
async readConfig(baseDirectory) {
|
|
5033
|
-
const configPath =
|
|
5472
|
+
const configPath = path6.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5034
5473
|
try {
|
|
5035
5474
|
const configContent = await fs5.readFile(configPath, "utf-8");
|
|
5036
5475
|
const config = JSON.parse(configContent);
|
|
@@ -5044,9 +5483,11 @@ var ConfigFileRepository = class {
|
|
|
5044
5483
|
if (error.code === "ENOENT") {
|
|
5045
5484
|
return null;
|
|
5046
5485
|
}
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5486
|
+
if (!this.warnedFiles.has(configPath)) {
|
|
5487
|
+
this.warnedFiles.add(configPath);
|
|
5488
|
+
logWarningConsole(`\u26A0 Skipping malformed config file: ${configPath}`);
|
|
5489
|
+
}
|
|
5490
|
+
return null;
|
|
5050
5491
|
}
|
|
5051
5492
|
}
|
|
5052
5493
|
/**
|
|
@@ -5057,7 +5498,7 @@ var ConfigFileRepository = class {
|
|
|
5057
5498
|
* @returns Array of directory paths that contain a packmind.json file
|
|
5058
5499
|
*/
|
|
5059
5500
|
async findDescendantConfigs(directory) {
|
|
5060
|
-
const normalizedDir =
|
|
5501
|
+
const normalizedDir = path6.resolve(directory);
|
|
5061
5502
|
const results = [];
|
|
5062
5503
|
const searchRecursively = async (currentDir) => {
|
|
5063
5504
|
let entries;
|
|
@@ -5073,7 +5514,7 @@ var ConfigFileRepository = class {
|
|
|
5073
5514
|
if (this.EXCLUDED_DIRECTORIES.includes(entry.name)) {
|
|
5074
5515
|
continue;
|
|
5075
5516
|
}
|
|
5076
|
-
const entryPath =
|
|
5517
|
+
const entryPath = path6.join(currentDir, entry.name);
|
|
5077
5518
|
const config = await this.readConfig(entryPath);
|
|
5078
5519
|
if (config) {
|
|
5079
5520
|
results.push(entryPath);
|
|
@@ -5095,19 +5536,19 @@ var ConfigFileRepository = class {
|
|
|
5095
5536
|
async readHierarchicalConfig(startDirectory, stopDirectory) {
|
|
5096
5537
|
const configs = [];
|
|
5097
5538
|
const configPaths = [];
|
|
5098
|
-
const normalizedStart =
|
|
5099
|
-
const normalizedStop = stopDirectory ?
|
|
5539
|
+
const normalizedStart = path6.resolve(startDirectory);
|
|
5540
|
+
const normalizedStop = stopDirectory ? path6.resolve(stopDirectory) : null;
|
|
5100
5541
|
let currentDir = normalizedStart;
|
|
5101
5542
|
while (true) {
|
|
5102
5543
|
const config = await this.readConfig(currentDir);
|
|
5103
5544
|
if (config) {
|
|
5104
5545
|
configs.push(config);
|
|
5105
|
-
configPaths.push(
|
|
5546
|
+
configPaths.push(path6.join(currentDir, this.CONFIG_FILENAME));
|
|
5106
5547
|
}
|
|
5107
5548
|
if (normalizedStop !== null && currentDir === normalizedStop) {
|
|
5108
5549
|
break;
|
|
5109
5550
|
}
|
|
5110
|
-
const parentDir =
|
|
5551
|
+
const parentDir = path6.dirname(currentDir);
|
|
5111
5552
|
if (parentDir === currentDir) {
|
|
5112
5553
|
break;
|
|
5113
5554
|
}
|
|
@@ -5127,6 +5568,82 @@ var ConfigFileRepository = class {
|
|
|
5127
5568
|
hasConfigs: configs.length > 0
|
|
5128
5569
|
};
|
|
5129
5570
|
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Finds all packmind.json files in the tree (both ancestors and descendants)
|
|
5573
|
+
* and returns each config with its target path.
|
|
5574
|
+
*
|
|
5575
|
+
* @param startDirectory - Directory to start searching from (typically the lint target)
|
|
5576
|
+
* @param stopDirectory - Directory to stop ancestor search at (typically git repo root), also used as base for descendants search
|
|
5577
|
+
* @returns All configs found with their target paths
|
|
5578
|
+
*/
|
|
5579
|
+
async findAllConfigsInTree(startDirectory, stopDirectory) {
|
|
5580
|
+
const normalizedStart = path6.resolve(startDirectory);
|
|
5581
|
+
const normalizedStop = stopDirectory ? path6.resolve(stopDirectory) : null;
|
|
5582
|
+
const basePath = normalizedStop ?? normalizedStart;
|
|
5583
|
+
const configsMap = /* @__PURE__ */ new Map();
|
|
5584
|
+
let currentDir = normalizedStart;
|
|
5585
|
+
while (true) {
|
|
5586
|
+
const config = await this.readConfig(currentDir);
|
|
5587
|
+
if (config) {
|
|
5588
|
+
const targetPath = this.computeRelativeTargetPath(currentDir, basePath);
|
|
5589
|
+
configsMap.set(currentDir, {
|
|
5590
|
+
targetPath,
|
|
5591
|
+
absoluteTargetPath: currentDir,
|
|
5592
|
+
packages: config.packages
|
|
5593
|
+
});
|
|
5594
|
+
}
|
|
5595
|
+
if (normalizedStop !== null && currentDir === normalizedStop) {
|
|
5596
|
+
break;
|
|
5597
|
+
}
|
|
5598
|
+
const parentDir = path6.dirname(currentDir);
|
|
5599
|
+
if (parentDir === currentDir) {
|
|
5600
|
+
break;
|
|
5601
|
+
}
|
|
5602
|
+
currentDir = parentDir;
|
|
5603
|
+
}
|
|
5604
|
+
const searchRoot = normalizedStop ?? normalizedStart;
|
|
5605
|
+
const descendantDirs = await this.findDescendantConfigs(searchRoot);
|
|
5606
|
+
for (const descendantDir of descendantDirs) {
|
|
5607
|
+
if (configsMap.has(descendantDir)) {
|
|
5608
|
+
continue;
|
|
5609
|
+
}
|
|
5610
|
+
const config = await this.readConfig(descendantDir);
|
|
5611
|
+
if (config) {
|
|
5612
|
+
const targetPath = this.computeRelativeTargetPath(
|
|
5613
|
+
descendantDir,
|
|
5614
|
+
basePath
|
|
5615
|
+
);
|
|
5616
|
+
configsMap.set(descendantDir, {
|
|
5617
|
+
targetPath,
|
|
5618
|
+
absoluteTargetPath: descendantDir,
|
|
5619
|
+
packages: config.packages
|
|
5620
|
+
});
|
|
5621
|
+
}
|
|
5622
|
+
}
|
|
5623
|
+
if (!configsMap.has(searchRoot)) {
|
|
5624
|
+
const rootConfig = await this.readConfig(searchRoot);
|
|
5625
|
+
if (rootConfig) {
|
|
5626
|
+
configsMap.set(searchRoot, {
|
|
5627
|
+
targetPath: "/",
|
|
5628
|
+
absoluteTargetPath: searchRoot,
|
|
5629
|
+
packages: rootConfig.packages
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
const configs = Array.from(configsMap.values());
|
|
5634
|
+
return {
|
|
5635
|
+
configs,
|
|
5636
|
+
hasConfigs: configs.length > 0,
|
|
5637
|
+
basePath
|
|
5638
|
+
};
|
|
5639
|
+
}
|
|
5640
|
+
computeRelativeTargetPath(absolutePath, basePath) {
|
|
5641
|
+
if (absolutePath === basePath) {
|
|
5642
|
+
return "/";
|
|
5643
|
+
}
|
|
5644
|
+
const relativePath = absolutePath.substring(basePath.length);
|
|
5645
|
+
return relativePath.startsWith("/") ? relativePath : "/" + relativePath;
|
|
5646
|
+
}
|
|
5130
5647
|
};
|
|
5131
5648
|
|
|
5132
5649
|
// apps/cli/src/PackmindCliHexaFactory.ts
|
|
@@ -5142,7 +5659,8 @@ var PackmindCliHexaFactory = class {
|
|
|
5142
5659
|
this.services = {
|
|
5143
5660
|
listFiles: new ListFiles(),
|
|
5144
5661
|
gitRemoteUrlService: new GitService(this.logger),
|
|
5145
|
-
linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
|
|
5662
|
+
linterExecutionUseCase: new ExecuteLinterProgramsUseCase(),
|
|
5663
|
+
diffViolationFilterService: new DiffViolationFilterService()
|
|
5146
5664
|
};
|
|
5147
5665
|
this.useCases = {
|
|
5148
5666
|
executeSingleFileAst: new ExecuteSingleFileAstUseCase(
|
|
@@ -5238,8 +5756,8 @@ var PackmindCliHexa = class {
|
|
|
5238
5756
|
(version) => version !== "*"
|
|
5239
5757
|
);
|
|
5240
5758
|
if (hasNonWildcardVersions) {
|
|
5241
|
-
|
|
5242
|
-
"
|
|
5759
|
+
logWarningConsole(
|
|
5760
|
+
"Package versions are not supported yet, getting the latest version"
|
|
5243
5761
|
);
|
|
5244
5762
|
}
|
|
5245
5763
|
return Object.keys(config.packages);
|
|
@@ -5284,7 +5802,6 @@ var IDELintLogger = class {
|
|
|
5284
5802
|
};
|
|
5285
5803
|
|
|
5286
5804
|
// apps/cli/src/infra/repositories/HumanReadableLogger.ts
|
|
5287
|
-
var import_chalk = __toESM(require("chalk"));
|
|
5288
5805
|
var HumanReadableLogger = class {
|
|
5289
5806
|
logViolations(violations) {
|
|
5290
5807
|
violations.forEach((violation) => {
|
|
@@ -5295,24 +5812,18 @@ var HumanReadableLogger = class {
|
|
|
5295
5812
|
(acc, violation) => acc + violation.violations.length,
|
|
5296
5813
|
0
|
|
5297
5814
|
);
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
import_chalk.default.red(
|
|
5301
|
-
`\u274C Found ${import_chalk.default.bold(totalViolationCount)} violation(s) in ${import_chalk.default.bold(violations.length)} file(s)`
|
|
5302
|
-
)
|
|
5815
|
+
logErrorConsole(
|
|
5816
|
+
`\u274C Found ${formatBold(String(totalViolationCount))} violation(s) in ${formatBold(String(violations.length))} file(s)`
|
|
5303
5817
|
);
|
|
5304
5818
|
} else {
|
|
5305
|
-
|
|
5306
|
-
import_chalk.default.bgGreen.bold("packmind-cli"),
|
|
5307
|
-
import_chalk.default.green.bold(`\u2705 No violations found`)
|
|
5308
|
-
);
|
|
5819
|
+
logSuccessConsole(`\u2705 No violations found`);
|
|
5309
5820
|
}
|
|
5310
5821
|
}
|
|
5311
5822
|
logViolation(violation) {
|
|
5312
|
-
console.log(
|
|
5823
|
+
console.log(formatFilePath(violation.file));
|
|
5313
5824
|
violation.violations.forEach(({ line, character, standard, rule }) => {
|
|
5314
5825
|
console.log(
|
|
5315
|
-
|
|
5826
|
+
formatError(` ${line}:${character} error @${standard}/${rule}`)
|
|
5316
5827
|
);
|
|
5317
5828
|
});
|
|
5318
5829
|
}
|
|
@@ -5320,6 +5831,99 @@ var HumanReadableLogger = class {
|
|
|
5320
5831
|
|
|
5321
5832
|
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
5322
5833
|
var pathModule = __toESM(require("path"));
|
|
5834
|
+
|
|
5835
|
+
// apps/cli/src/infra/commands/lintHandler.ts
|
|
5836
|
+
var MISSING_API_KEY_ERROR = "Please set the PACKMIND_API_KEY_V3 environment variable";
|
|
5837
|
+
function isMissingApiKeyError(error) {
|
|
5838
|
+
if (error instanceof Error) {
|
|
5839
|
+
return error.message.includes(MISSING_API_KEY_ERROR);
|
|
5840
|
+
}
|
|
5841
|
+
return false;
|
|
5842
|
+
}
|
|
5843
|
+
async function lintHandler(args2, deps) {
|
|
5844
|
+
const {
|
|
5845
|
+
path: path8,
|
|
5846
|
+
draft,
|
|
5847
|
+
rule,
|
|
5848
|
+
language,
|
|
5849
|
+
logger: logger2,
|
|
5850
|
+
continueOnError,
|
|
5851
|
+
continueOnMissingKey,
|
|
5852
|
+
diff
|
|
5853
|
+
} = args2;
|
|
5854
|
+
const {
|
|
5855
|
+
packmindCliHexa,
|
|
5856
|
+
humanReadableLogger,
|
|
5857
|
+
ideLintLogger,
|
|
5858
|
+
resolvePath,
|
|
5859
|
+
exit
|
|
5860
|
+
} = deps;
|
|
5861
|
+
if (draft && !rule) {
|
|
5862
|
+
throw new Error("option --rule is required to use --draft mode");
|
|
5863
|
+
}
|
|
5864
|
+
const startedAt = Date.now();
|
|
5865
|
+
const targetPath = path8 ?? ".";
|
|
5866
|
+
const hasArguments = !!(draft || rule || language);
|
|
5867
|
+
const absolutePath = resolvePath(targetPath);
|
|
5868
|
+
if (diff) {
|
|
5869
|
+
const gitRoot = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
|
|
5870
|
+
if (!gitRoot) {
|
|
5871
|
+
throw new Error(
|
|
5872
|
+
"The --diff option requires the project to be in a Git repository"
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
let useLocalLinting = false;
|
|
5877
|
+
if (!hasArguments) {
|
|
5878
|
+
const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
|
|
5879
|
+
const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5880
|
+
absolutePath,
|
|
5881
|
+
stopDirectory
|
|
5882
|
+
);
|
|
5883
|
+
if (hierarchicalConfig.hasConfigs) {
|
|
5884
|
+
useLocalLinting = true;
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
let violations = [];
|
|
5888
|
+
try {
|
|
5889
|
+
if (useLocalLinting) {
|
|
5890
|
+
const result = await packmindCliHexa.lintFilesLocally({
|
|
5891
|
+
path: absolutePath,
|
|
5892
|
+
diffMode: diff
|
|
5893
|
+
});
|
|
5894
|
+
violations = result.violations;
|
|
5895
|
+
} else {
|
|
5896
|
+
const result = await packmindCliHexa.lintFilesInDirectory({
|
|
5897
|
+
path: targetPath,
|
|
5898
|
+
draftMode: draft,
|
|
5899
|
+
standardSlug: rule?.standardSlug,
|
|
5900
|
+
ruleId: rule?.ruleId,
|
|
5901
|
+
language,
|
|
5902
|
+
diffMode: diff
|
|
5903
|
+
});
|
|
5904
|
+
violations = result.violations;
|
|
5905
|
+
}
|
|
5906
|
+
} catch (error) {
|
|
5907
|
+
if (isMissingApiKeyError(error) && continueOnMissingKey) {
|
|
5908
|
+
console.warn("Warning: No PACKMIND_API_KEY_V3 set, linting is skipped.");
|
|
5909
|
+
exit(0);
|
|
5910
|
+
return;
|
|
5911
|
+
}
|
|
5912
|
+
throw error;
|
|
5913
|
+
}
|
|
5914
|
+
(logger2 === "ide" /* ide */ ? ideLintLogger : humanReadableLogger).logViolations(
|
|
5915
|
+
violations
|
|
5916
|
+
);
|
|
5917
|
+
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
5918
|
+
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
5919
|
+
if (violations.length > 0 && !continueOnError) {
|
|
5920
|
+
exit(1);
|
|
5921
|
+
} else {
|
|
5922
|
+
exit(0);
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
|
|
5926
|
+
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
5323
5927
|
var Logger = {
|
|
5324
5928
|
from: async (input) => {
|
|
5325
5929
|
switch (input) {
|
|
@@ -5347,6 +5951,19 @@ var RuleID = {
|
|
|
5347
5951
|
};
|
|
5348
5952
|
}
|
|
5349
5953
|
};
|
|
5954
|
+
var DiffModeType = {
|
|
5955
|
+
from: async (input) => {
|
|
5956
|
+
switch (input) {
|
|
5957
|
+
case "files":
|
|
5958
|
+
return "files" /* FILES */;
|
|
5959
|
+
case "lines":
|
|
5960
|
+
return "lines" /* LINES */;
|
|
5961
|
+
}
|
|
5962
|
+
throw new Error(
|
|
5963
|
+
`${input} is not a valid value for the --diff option. Expected values are: files, lines`
|
|
5964
|
+
);
|
|
5965
|
+
}
|
|
5966
|
+
};
|
|
5350
5967
|
var lintCommand = (0, import_cmd_ts.command)({
|
|
5351
5968
|
name: "lint",
|
|
5352
5969
|
description: "Lint code at the specified path",
|
|
@@ -5380,72 +5997,34 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
5380
5997
|
debug: (0, import_cmd_ts.flag)({
|
|
5381
5998
|
long: "debug",
|
|
5382
5999
|
description: "Enable debug logging"
|
|
6000
|
+
}),
|
|
6001
|
+
continueOnError: (0, import_cmd_ts.flag)({
|
|
6002
|
+
long: "continue-on-error",
|
|
6003
|
+
description: "Exit with status code 0 even if violations are found"
|
|
6004
|
+
}),
|
|
6005
|
+
continueOnMissingKey: (0, import_cmd_ts.flag)({
|
|
6006
|
+
long: "continue-on-missing-key",
|
|
6007
|
+
description: "Skip linting and exit with status code 0 if PACKMIND_API_KEY_V3 is not set"
|
|
6008
|
+
}),
|
|
6009
|
+
diff: (0, import_cmd_ts.option)({
|
|
6010
|
+
long: "diff",
|
|
6011
|
+
description: "Filter violations by git diff (files | lines)",
|
|
6012
|
+
type: (0, import_cmd_ts.optional)(DiffModeType)
|
|
5383
6013
|
})
|
|
5384
6014
|
},
|
|
5385
|
-
handler: async (
|
|
5386
|
-
if (draft && !rule) {
|
|
5387
|
-
throw new Error("option --rule is required to use --draft mode");
|
|
5388
|
-
}
|
|
5389
|
-
const startedAt = Date.now();
|
|
6015
|
+
handler: async (args2) => {
|
|
5390
6016
|
const packmindLogger = new PackmindLogger(
|
|
5391
6017
|
"PackmindCLI",
|
|
5392
|
-
debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
6018
|
+
args2.debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
5393
6019
|
);
|
|
5394
|
-
const
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5403
|
-
absolutePath,
|
|
5404
|
-
stopDirectory
|
|
5405
|
-
);
|
|
5406
|
-
if (hierarchicalConfig.hasConfigs) {
|
|
5407
|
-
useLocalLinting = true;
|
|
5408
|
-
const rootConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5409
|
-
absolutePath,
|
|
5410
|
-
absolutePath
|
|
5411
|
-
);
|
|
5412
|
-
if (rootConfig.hasConfigs) {
|
|
5413
|
-
lintTargets.push(absolutePath);
|
|
5414
|
-
}
|
|
5415
|
-
const descendantTargets = await packmindCliHexa.findDescendantConfigs(absolutePath);
|
|
5416
|
-
lintTargets = [...lintTargets, ...descendantTargets];
|
|
5417
|
-
if (lintTargets.length === 0) {
|
|
5418
|
-
lintTargets.push(absolutePath);
|
|
5419
|
-
}
|
|
5420
|
-
}
|
|
5421
|
-
}
|
|
5422
|
-
let violations = [];
|
|
5423
|
-
if (useLocalLinting && lintTargets.length > 0) {
|
|
5424
|
-
for (const target of lintTargets) {
|
|
5425
|
-
packmindLogger.debug(`Linting target: ${target}`);
|
|
5426
|
-
const result = await packmindCliHexa.lintFilesLocally({
|
|
5427
|
-
path: target
|
|
5428
|
-
});
|
|
5429
|
-
violations = [...violations, ...result.violations];
|
|
5430
|
-
}
|
|
5431
|
-
} else {
|
|
5432
|
-
const result = await packmindCliHexa.lintFilesInDirectory({
|
|
5433
|
-
path: targetPath,
|
|
5434
|
-
draftMode: draft,
|
|
5435
|
-
standardSlug: rule?.standardSlug,
|
|
5436
|
-
ruleId: rule?.ruleId,
|
|
5437
|
-
language
|
|
5438
|
-
});
|
|
5439
|
-
violations = result.violations;
|
|
5440
|
-
}
|
|
5441
|
-
(logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
|
|
5442
|
-
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
5443
|
-
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
5444
|
-
if (violations.length > 0) {
|
|
5445
|
-
process.exit(1);
|
|
5446
|
-
} else {
|
|
5447
|
-
process.exit(0);
|
|
5448
|
-
}
|
|
6020
|
+
const deps = {
|
|
6021
|
+
packmindCliHexa: new PackmindCliHexa(packmindLogger),
|
|
6022
|
+
humanReadableLogger: new HumanReadableLogger(),
|
|
6023
|
+
ideLintLogger: new IDELintLogger(),
|
|
6024
|
+
resolvePath: (targetPath) => pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath),
|
|
6025
|
+
exit: (code) => process.exit(code)
|
|
6026
|
+
};
|
|
6027
|
+
await lintHandler(args2, deps);
|
|
5449
6028
|
}
|
|
5450
6029
|
});
|
|
5451
6030
|
|
|
@@ -5506,7 +6085,7 @@ function extractWasmFiles() {
|
|
|
5506
6085
|
// apps/cli/src/main.ts
|
|
5507
6086
|
var import_dotenv = require("dotenv");
|
|
5508
6087
|
var fs6 = __toESM(require("fs"));
|
|
5509
|
-
var
|
|
6088
|
+
var path7 = __toESM(require("path"));
|
|
5510
6089
|
|
|
5511
6090
|
// apps/cli/src/infra/commands/PullCommand.ts
|
|
5512
6091
|
var import_cmd_ts2 = require("cmd-ts");
|
|
@@ -5541,14 +6120,28 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
5541
6120
|
console.log("No packages found.");
|
|
5542
6121
|
process.exit(0);
|
|
5543
6122
|
}
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
6123
|
+
const sortedPackages = [...packages].sort(
|
|
6124
|
+
(a, b) => a.slug.localeCompare(b.slug)
|
|
6125
|
+
);
|
|
6126
|
+
console.log("Available packages:\n");
|
|
6127
|
+
sortedPackages.forEach((pkg, index) => {
|
|
6128
|
+
console.log(`- ${formatSlug(pkg.slug)}`);
|
|
6129
|
+
console.log(` ${formatLabel("Name:")} ${pkg.name}`);
|
|
5547
6130
|
if (pkg.description) {
|
|
5548
|
-
|
|
6131
|
+
const descriptionLines = pkg.description.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
6132
|
+
const [firstLine, ...restLines] = descriptionLines;
|
|
6133
|
+
console.log(` ${formatLabel("Description:")} ${firstLine}`);
|
|
6134
|
+
restLines.forEach((line) => {
|
|
6135
|
+
console.log(` ${line}`);
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
if (index < sortedPackages.length - 1) {
|
|
5549
6139
|
console.log("");
|
|
5550
6140
|
}
|
|
5551
6141
|
});
|
|
6142
|
+
const exampleSlug = formatSlug(sortedPackages[0].slug);
|
|
6143
|
+
console.log("\nHow to install a package:\n");
|
|
6144
|
+
console.log(` $ packmind-cli install ${exampleSlug}`);
|
|
5552
6145
|
process.exit(0);
|
|
5553
6146
|
} catch (error) {
|
|
5554
6147
|
console.error("\n\u274C Failed to list packages:");
|
|
@@ -5623,7 +6216,7 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
5623
6216
|
}
|
|
5624
6217
|
const allPackages = [.../* @__PURE__ */ new Set([...configPackages, ...packagesSlugs])];
|
|
5625
6218
|
if (allPackages.length === 0) {
|
|
5626
|
-
|
|
6219
|
+
logWarningConsole("config packmind.json not found");
|
|
5627
6220
|
console.log(
|
|
5628
6221
|
"Usage: packmind-cli install <package-slug> [package-slug...]"
|
|
5629
6222
|
);
|
|
@@ -5737,29 +6330,23 @@ added ${result.filesCreated} files, changed ${result.filesUpdated} files, remove
|
|
|
5737
6330
|
// apps/cli/src/main.ts
|
|
5738
6331
|
var { version: CLI_VERSION } = require_package();
|
|
5739
6332
|
function findEnvFile() {
|
|
5740
|
-
|
|
5741
|
-
const
|
|
5742
|
-
|
|
6333
|
+
const currentDir = process.cwd();
|
|
6334
|
+
const gitService = new GitService();
|
|
6335
|
+
const gitRoot = gitService.getGitRepositoryRootSync(currentDir);
|
|
6336
|
+
const filesystemRoot = path7.parse(currentDir).root;
|
|
6337
|
+
const stopDir = gitRoot ?? filesystemRoot;
|
|
5743
6338
|
let searchDir = currentDir;
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
break;
|
|
5748
|
-
}
|
|
5749
|
-
searchDir = path6.dirname(searchDir);
|
|
5750
|
-
}
|
|
5751
|
-
while (currentDir !== path6.parse(currentDir).root) {
|
|
5752
|
-
const envPath2 = path6.join(currentDir, ".env");
|
|
6339
|
+
let parentDir = path7.dirname(searchDir);
|
|
6340
|
+
while (searchDir !== parentDir) {
|
|
6341
|
+
const envPath2 = path7.join(searchDir, ".env");
|
|
5753
6342
|
if (fs6.existsSync(envPath2)) {
|
|
5754
6343
|
return envPath2;
|
|
5755
6344
|
}
|
|
5756
|
-
if (
|
|
5757
|
-
|
|
5758
|
-
}
|
|
5759
|
-
if (!gitRootFound && currentDir !== startDir) {
|
|
5760
|
-
break;
|
|
6345
|
+
if (searchDir === stopDir) {
|
|
6346
|
+
return null;
|
|
5761
6347
|
}
|
|
5762
|
-
|
|
6348
|
+
searchDir = parentDir;
|
|
6349
|
+
parentDir = path7.dirname(searchDir);
|
|
5763
6350
|
}
|
|
5764
6351
|
return null;
|
|
5765
6352
|
}
|
|
@@ -5789,6 +6376,6 @@ var app = (0, import_cmd_ts3.subcommands)({
|
|
|
5789
6376
|
}
|
|
5790
6377
|
});
|
|
5791
6378
|
(0, import_cmd_ts3.run)(app, args).catch((error) => {
|
|
5792
|
-
|
|
6379
|
+
logErrorConsole(error.message);
|
|
5793
6380
|
process.exit(1);
|
|
5794
6381
|
});
|