@living-architecture/riviere-cli 0.8.12 → 0.9.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/dist/bin.js +610 -435
- package/dist/index.js +614 -436
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -23540,19 +23540,12 @@ async function fileExists(path2) {
|
|
|
23540
23540
|
}
|
|
23541
23541
|
|
|
23542
23542
|
// src/platform/infra/cli-presentation/error-codes.ts
|
|
23543
|
-
var ExitCode = /* @__PURE__ */ ((
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
return
|
|
23543
|
+
var ExitCode = /* @__PURE__ */ ((ExitCode3) => {
|
|
23544
|
+
ExitCode3[ExitCode3["ExtractionFailure"] = 1] = "ExtractionFailure";
|
|
23545
|
+
ExitCode3[ExitCode3["ConfigValidation"] = 2] = "ConfigValidation";
|
|
23546
|
+
ExitCode3[ExitCode3["RuntimeError"] = 3] = "RuntimeError";
|
|
23547
|
+
return ExitCode3;
|
|
23548
23548
|
})(ExitCode || {});
|
|
23549
|
-
var ExtractionFieldFailureError = class extends Error {
|
|
23550
|
-
constructor(failedFields) {
|
|
23551
|
-
const uniqueFields = [...new Set(failedFields)];
|
|
23552
|
-
super(`Extraction failed for fields: ${uniqueFields.join(", ")}`);
|
|
23553
|
-
this.name = "ExtractionFieldFailureError";
|
|
23554
|
-
}
|
|
23555
|
-
};
|
|
23556
23549
|
var ConfigValidationError = class extends Error {
|
|
23557
23550
|
errorCode;
|
|
23558
23551
|
constructor(code, message) {
|
|
@@ -25201,6 +25194,69 @@ Examples:
|
|
|
25201
25194
|
// src/features/extract/entrypoint/extract.ts
|
|
25202
25195
|
import { Command as Command21 } from "commander";
|
|
25203
25196
|
|
|
25197
|
+
// src/platform/infra/cli-presentation/exit-with-cli-error.ts
|
|
25198
|
+
function exitWithCliError(code, message, exitCode) {
|
|
25199
|
+
console.log(JSON.stringify(formatError2(code, message)));
|
|
25200
|
+
process.exit(exitCode);
|
|
25201
|
+
}
|
|
25202
|
+
|
|
25203
|
+
// src/platform/infra/cli-presentation/extract-validator.ts
|
|
25204
|
+
function rejectMutuallyExclusive(flagA, flagB, aPresent, bPresent) {
|
|
25205
|
+
if (aPresent && bPresent) {
|
|
25206
|
+
throw new ConfigValidationError(
|
|
25207
|
+
"VALIDATION_ERROR" /* ValidationError */,
|
|
25208
|
+
`${flagA} and ${flagB} cannot be used together`
|
|
25209
|
+
);
|
|
25210
|
+
}
|
|
25211
|
+
}
|
|
25212
|
+
function validateMutualExclusions(options) {
|
|
25213
|
+
rejectMutuallyExclusive(
|
|
25214
|
+
"--components-only",
|
|
25215
|
+
"--enrich",
|
|
25216
|
+
options.componentsOnly === true,
|
|
25217
|
+
options.enrich !== void 0
|
|
25218
|
+
);
|
|
25219
|
+
rejectMutuallyExclusive("--pr", "--files", options.pr === true, options.files !== void 0);
|
|
25220
|
+
rejectMutuallyExclusive("--pr", "--enrich", options.pr === true, options.enrich !== void 0);
|
|
25221
|
+
rejectMutuallyExclusive(
|
|
25222
|
+
"--files",
|
|
25223
|
+
"--enrich",
|
|
25224
|
+
options.files !== void 0,
|
|
25225
|
+
options.enrich !== void 0
|
|
25226
|
+
);
|
|
25227
|
+
}
|
|
25228
|
+
function validateFormatOption(options) {
|
|
25229
|
+
if (options.format !== void 0 && options.format !== "json" && options.format !== "markdown") {
|
|
25230
|
+
throw new ConfigValidationError(
|
|
25231
|
+
"VALIDATION_ERROR" /* ValidationError */,
|
|
25232
|
+
`Invalid format '${options.format}'. Must be 'json' or 'markdown'.`
|
|
25233
|
+
);
|
|
25234
|
+
}
|
|
25235
|
+
if (options.format === "markdown" && !options.pr && options.files === void 0) {
|
|
25236
|
+
throw new ConfigValidationError(
|
|
25237
|
+
"VALIDATION_ERROR" /* ValidationError */,
|
|
25238
|
+
"--format markdown requires --pr or --files"
|
|
25239
|
+
);
|
|
25240
|
+
}
|
|
25241
|
+
}
|
|
25242
|
+
function validateFlagCombinations(options) {
|
|
25243
|
+
validateMutualExclusions(options);
|
|
25244
|
+
if (options.base !== void 0 && !options.pr) {
|
|
25245
|
+
throw new ConfigValidationError(
|
|
25246
|
+
"VALIDATION_ERROR" /* ValidationError */,
|
|
25247
|
+
"--base can only be used with --pr"
|
|
25248
|
+
);
|
|
25249
|
+
}
|
|
25250
|
+
validateFormatOption(options);
|
|
25251
|
+
}
|
|
25252
|
+
|
|
25253
|
+
// src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts
|
|
25254
|
+
import {
|
|
25255
|
+
posix as posix5,
|
|
25256
|
+
resolve as resolve7
|
|
25257
|
+
} from "node:path";
|
|
25258
|
+
import { globSync as globSync2 } from "glob";
|
|
25259
|
+
|
|
25204
25260
|
// src/platform/infra/extraction-config/config-loader.ts
|
|
25205
25261
|
import {
|
|
25206
25262
|
dirname as dirname2,
|
|
@@ -29725,13 +29781,46 @@ ${formatValidationErrors2(validationResult.errors)}`
|
|
|
29725
29781
|
};
|
|
29726
29782
|
}
|
|
29727
29783
|
|
|
29728
|
-
// src/platform/infra/
|
|
29729
|
-
import {
|
|
29730
|
-
|
|
29784
|
+
// src/platform/infra/extraction-config/draft-component-loader.ts
|
|
29785
|
+
import {
|
|
29786
|
+
existsSync as existsSync3,
|
|
29787
|
+
readFileSync as readFileSync3
|
|
29788
|
+
} from "node:fs";
|
|
29789
|
+
var DraftComponentLoadError = class extends Error {
|
|
29790
|
+
constructor(message) {
|
|
29791
|
+
super(message);
|
|
29792
|
+
this.name = "DraftComponentLoadError";
|
|
29793
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
29794
|
+
}
|
|
29795
|
+
};
|
|
29796
|
+
function isDraftComponentArray(value) {
|
|
29797
|
+
if (!Array.isArray(value)) return false;
|
|
29798
|
+
return value.every(
|
|
29799
|
+
(item) => typeof item === "object" && item !== null && "type" in item && "name" in item && "domain" in item && "location" in item
|
|
29800
|
+
);
|
|
29801
|
+
}
|
|
29802
|
+
function parseJsonFile(filePath) {
|
|
29803
|
+
try {
|
|
29804
|
+
return JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
29805
|
+
} catch {
|
|
29806
|
+
throw new DraftComponentLoadError(`Enrich file contains invalid JSON: ${filePath}`);
|
|
29807
|
+
}
|
|
29808
|
+
}
|
|
29809
|
+
function loadDraftComponentsFromFile(filePath) {
|
|
29810
|
+
if (!existsSync3(filePath)) {
|
|
29811
|
+
throw new DraftComponentLoadError(`Enrich file not found: ${filePath}`);
|
|
29812
|
+
}
|
|
29813
|
+
const parsed = parseJsonFile(filePath);
|
|
29814
|
+
if (!isDraftComponentArray(parsed)) {
|
|
29815
|
+
throw new DraftComponentLoadError(
|
|
29816
|
+
`Enrich file does not contain valid draft components: ${filePath}`
|
|
29817
|
+
);
|
|
29818
|
+
}
|
|
29819
|
+
return parsed;
|
|
29820
|
+
}
|
|
29731
29821
|
|
|
29732
|
-
// src/platform/infra/git/git-
|
|
29822
|
+
// src/platform/infra/git/git-repository-info.ts
|
|
29733
29823
|
import { execFileSync } from "node:child_process";
|
|
29734
|
-
import { resolve as resolve3 } from "node:path";
|
|
29735
29824
|
|
|
29736
29825
|
// src/platform/infra/git/git-errors.ts
|
|
29737
29826
|
var GitError = class extends Error {
|
|
@@ -29743,8 +29832,22 @@ var GitError = class extends Error {
|
|
|
29743
29832
|
Error.captureStackTrace?.(this, this.constructor);
|
|
29744
29833
|
}
|
|
29745
29834
|
};
|
|
29746
|
-
|
|
29747
|
-
|
|
29835
|
+
|
|
29836
|
+
// src/platform/infra/git/git-repository-info.ts
|
|
29837
|
+
var RepositoryUrlParseError = class extends Error {
|
|
29838
|
+
/* v8 ignore start -- @preserve: Error constructor; tested via integration */
|
|
29839
|
+
constructor(url2) {
|
|
29840
|
+
super(`Expected owner and repo in git URL, got ${url2}`);
|
|
29841
|
+
this.name = "RepositoryUrlParseError";
|
|
29842
|
+
}
|
|
29843
|
+
/* v8 ignore stop */
|
|
29844
|
+
};
|
|
29845
|
+
function defaultGitExecutor(binary, args, cwd) {
|
|
29846
|
+
return execFileSync(binary, args, {
|
|
29847
|
+
cwd,
|
|
29848
|
+
encoding: "utf-8",
|
|
29849
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
29850
|
+
}).trim();
|
|
29748
29851
|
}
|
|
29749
29852
|
function extractStderr(error48) {
|
|
29750
29853
|
if (!Object.hasOwn(error48, "stderr")) {
|
|
@@ -29756,16 +29859,98 @@ function extractStderr(error48) {
|
|
|
29756
29859
|
}
|
|
29757
29860
|
return String(stderrValue);
|
|
29758
29861
|
}
|
|
29862
|
+
function runGit(executor, gitBinary, cwd, args) {
|
|
29863
|
+
try {
|
|
29864
|
+
return executor(gitBinary, args, cwd);
|
|
29865
|
+
} catch (error48) {
|
|
29866
|
+
if (error48 instanceof Error && "code" in error48 && error48.code === "ENOENT") {
|
|
29867
|
+
throw new GitError("GIT_NOT_FOUND", "Install git to detect repository information.");
|
|
29868
|
+
}
|
|
29869
|
+
if (error48 instanceof Error) {
|
|
29870
|
+
const stderr = extractStderr(error48);
|
|
29871
|
+
if (stderr.includes("not a git repository")) {
|
|
29872
|
+
throw new GitError("NOT_A_REPOSITORY", "Run from within a git repository.");
|
|
29873
|
+
}
|
|
29874
|
+
}
|
|
29875
|
+
throw error48;
|
|
29876
|
+
}
|
|
29877
|
+
}
|
|
29878
|
+
function parseRepositoryUrl(url2) {
|
|
29879
|
+
const sshRegex = /^git@[^:]+:([^/]+)\/(.+?)(?:\.git)?$/;
|
|
29880
|
+
const sshMatch = sshRegex.exec(url2);
|
|
29881
|
+
if (sshMatch) {
|
|
29882
|
+
const [, owner, repo] = sshMatch;
|
|
29883
|
+
if (!owner || !repo) {
|
|
29884
|
+
throw new RepositoryUrlParseError(url2);
|
|
29885
|
+
}
|
|
29886
|
+
return {
|
|
29887
|
+
name: `${owner}/${repo}`,
|
|
29888
|
+
owner,
|
|
29889
|
+
url: url2
|
|
29890
|
+
};
|
|
29891
|
+
}
|
|
29892
|
+
const httpsRegex = /^https?:\/\/[^/]+\/([^/]+)\/(.+?)(?:\.git)?$/;
|
|
29893
|
+
const httpsMatch = httpsRegex.exec(url2);
|
|
29894
|
+
if (httpsMatch) {
|
|
29895
|
+
const [, owner, repo] = httpsMatch;
|
|
29896
|
+
if (!owner || !repo) {
|
|
29897
|
+
throw new RepositoryUrlParseError(url2);
|
|
29898
|
+
}
|
|
29899
|
+
return {
|
|
29900
|
+
name: `${owner}/${repo}`,
|
|
29901
|
+
owner,
|
|
29902
|
+
url: url2
|
|
29903
|
+
};
|
|
29904
|
+
}
|
|
29905
|
+
return {
|
|
29906
|
+
name: url2,
|
|
29907
|
+
url: url2
|
|
29908
|
+
};
|
|
29909
|
+
}
|
|
29910
|
+
function getRepositoryInfo(gitBinary = "git", cwd = process.cwd(), executor = defaultGitExecutor) {
|
|
29911
|
+
try {
|
|
29912
|
+
const url2 = runGit(executor, gitBinary, cwd, ["remote", "get-url", "origin"]);
|
|
29913
|
+
return parseRepositoryUrl(url2);
|
|
29914
|
+
} catch (error48) {
|
|
29915
|
+
if (error48 instanceof GitError) throw error48;
|
|
29916
|
+
if (error48 instanceof Error) {
|
|
29917
|
+
const stderr = extractStderr(error48);
|
|
29918
|
+
if (stderr.includes("No such remote")) {
|
|
29919
|
+
throw new GitError("NO_REMOTE", 'No git remote named "origin" found.');
|
|
29920
|
+
}
|
|
29921
|
+
}
|
|
29922
|
+
throw error48;
|
|
29923
|
+
}
|
|
29924
|
+
}
|
|
29925
|
+
|
|
29926
|
+
// src/platform/infra/source-filtering/filter-source-files.ts
|
|
29927
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
29928
|
+
import { resolve as resolve4 } from "node:path";
|
|
29759
29929
|
|
|
29760
29930
|
// src/platform/infra/git/git-changed-files.ts
|
|
29761
|
-
|
|
29762
|
-
|
|
29931
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
29932
|
+
import { resolve as resolve3 } from "node:path";
|
|
29933
|
+
function extractStderr2(error48) {
|
|
29934
|
+
if (!Object.hasOwn(error48, "stderr")) {
|
|
29935
|
+
throw error48;
|
|
29936
|
+
}
|
|
29937
|
+
const stderrValue = Object.getOwnPropertyDescriptor(error48, "stderr")?.value;
|
|
29938
|
+
if (!stderrValue) {
|
|
29939
|
+
throw error48;
|
|
29940
|
+
}
|
|
29941
|
+
return String(stderrValue);
|
|
29942
|
+
}
|
|
29943
|
+
function isGitError(error48) {
|
|
29944
|
+
return error48 instanceof GitError;
|
|
29945
|
+
}
|
|
29946
|
+
function defaultGitExecutor2(binary, args, cwd) {
|
|
29947
|
+
return execFileSync2(binary, args, {
|
|
29763
29948
|
cwd,
|
|
29764
29949
|
encoding: "utf-8",
|
|
29765
29950
|
stdio: ["pipe", "pipe", "pipe"]
|
|
29766
29951
|
}).trim();
|
|
29767
29952
|
}
|
|
29768
|
-
function
|
|
29953
|
+
function runGit2(executor, gitBinary, cwd, args) {
|
|
29769
29954
|
try {
|
|
29770
29955
|
return executor(gitBinary, args, cwd);
|
|
29771
29956
|
} catch (error48) {
|
|
@@ -29773,7 +29958,7 @@ function runGit(executor, gitBinary, cwd, args) {
|
|
|
29773
29958
|
throw new GitError("GIT_NOT_FOUND", "Install git to use --pr flag.");
|
|
29774
29959
|
}
|
|
29775
29960
|
if (error48 instanceof Error) {
|
|
29776
|
-
const stderr =
|
|
29961
|
+
const stderr = extractStderr2(error48);
|
|
29777
29962
|
if (stderr.includes("not a git repository")) {
|
|
29778
29963
|
throw new GitError("NOT_A_REPOSITORY", "Run from within a git repository.");
|
|
29779
29964
|
}
|
|
@@ -29786,7 +29971,7 @@ function isTypeScriptFile(filePath) {
|
|
|
29786
29971
|
}
|
|
29787
29972
|
function isDetachedHead(executor, gitBinary, cwd) {
|
|
29788
29973
|
try {
|
|
29789
|
-
|
|
29974
|
+
runGit2(executor, gitBinary, cwd, ["symbolic-ref", "HEAD"]);
|
|
29790
29975
|
return false;
|
|
29791
29976
|
} catch {
|
|
29792
29977
|
return true;
|
|
@@ -29797,25 +29982,25 @@ function detectBaseBranch(executor, gitBinary, cwd, explicitBase) {
|
|
|
29797
29982
|
return explicitBase;
|
|
29798
29983
|
}
|
|
29799
29984
|
try {
|
|
29800
|
-
const ref =
|
|
29985
|
+
const ref = runGit2(executor, gitBinary, cwd, ["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
29801
29986
|
return ref.replace("refs/remotes/origin/", "");
|
|
29802
29987
|
} catch {
|
|
29803
29988
|
return "main";
|
|
29804
29989
|
}
|
|
29805
29990
|
}
|
|
29806
29991
|
function getUntrackedTypeScriptFiles(executor, gitBinary, cwd) {
|
|
29807
|
-
const output =
|
|
29992
|
+
const output = runGit2(executor, gitBinary, cwd, ["ls-files", "--others", "--exclude-standard"]);
|
|
29808
29993
|
if (output === "") return [];
|
|
29809
29994
|
return output.split("\n").filter(isTypeScriptFile);
|
|
29810
29995
|
}
|
|
29811
29996
|
function getStagedFiles(executor, gitBinary, cwd, base) {
|
|
29812
|
-
const output =
|
|
29997
|
+
const output = runGit2(executor, gitBinary, cwd, ["diff", "--name-only", "--cached", base]);
|
|
29813
29998
|
if (output === "") return [];
|
|
29814
29999
|
return output.split("\n").filter(isTypeScriptFile);
|
|
29815
30000
|
}
|
|
29816
30001
|
function getCommittedChangedFiles(executor, gitBinary, cwd, base) {
|
|
29817
30002
|
try {
|
|
29818
|
-
const output =
|
|
30003
|
+
const output = runGit2(executor, gitBinary, cwd, ["diff", "--name-only", `${base}...HEAD`]);
|
|
29819
30004
|
if (output === "") return [];
|
|
29820
30005
|
return output.split("\n").filter(isTypeScriptFile);
|
|
29821
30006
|
} catch (error48) {
|
|
@@ -29823,9 +30008,9 @@ function getCommittedChangedFiles(executor, gitBinary, cwd, base) {
|
|
|
29823
30008
|
throw new GitError("BASE_BRANCH_NOT_FOUND", `Base branch '${base}' not found.`);
|
|
29824
30009
|
}
|
|
29825
30010
|
}
|
|
29826
|
-
function detectChangedTypeScriptFiles(cwd, options, executor =
|
|
30011
|
+
function detectChangedTypeScriptFiles(cwd, options, executor = defaultGitExecutor2) {
|
|
29827
30012
|
const gitBinary = "git";
|
|
29828
|
-
|
|
30013
|
+
runGit2(executor, gitBinary, cwd, ["rev-parse", "--git-dir"]);
|
|
29829
30014
|
const warnings = [];
|
|
29830
30015
|
const detached = isDetachedHead(executor, gitBinary, cwd);
|
|
29831
30016
|
const base = detached ? "HEAD~1" : detectBaseBranch(executor, gitBinary, cwd, options.base);
|
|
@@ -29871,7 +30056,7 @@ function filterSourceFiles(allSourceFiles, options) {
|
|
|
29871
30056
|
}
|
|
29872
30057
|
}
|
|
29873
30058
|
if (options.files !== void 0) {
|
|
29874
|
-
const missingFiles = options.files.filter((f) => !
|
|
30059
|
+
const missingFiles = options.files.filter((f) => !existsSync4(resolve4(f)));
|
|
29875
30060
|
if (missingFiles.length > 0) {
|
|
29876
30061
|
throw new SourceFilterError("FILES_NOT_FOUND", `Files not found: ${missingFiles.join(", ")}`);
|
|
29877
30062
|
}
|
|
@@ -29884,203 +30069,191 @@ function resolveFilteredSourceFiles(allSourceFiles, options) {
|
|
|
29884
30069
|
return filterSourceFiles(allSourceFiles, options).files;
|
|
29885
30070
|
}
|
|
29886
30071
|
|
|
29887
|
-
// src/
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
29891
|
-
|
|
29892
|
-
|
|
30072
|
+
// src/features/extract/domain/extraction-project.ts
|
|
30073
|
+
import { posix as posix4 } from "node:path";
|
|
30074
|
+
var OrphanedDraftComponentError = class extends Error {
|
|
30075
|
+
constructor(orphanedModules, knownModules) {
|
|
30076
|
+
super(
|
|
30077
|
+
`Draft components reference unknown modules: [${orphanedModules.join(", ")}]. Known modules: [${knownModules.join(", ")}]`
|
|
29893
30078
|
);
|
|
30079
|
+
this.name = "OrphanedDraftComponentError";
|
|
29894
30080
|
}
|
|
29895
|
-
}
|
|
29896
|
-
|
|
29897
|
-
|
|
29898
|
-
|
|
29899
|
-
|
|
29900
|
-
|
|
29901
|
-
|
|
29902
|
-
|
|
29903
|
-
|
|
29904
|
-
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29910
|
-
|
|
29911
|
-
|
|
29912
|
-
|
|
29913
|
-
if (options.format !== void 0 && options.format !== "json" && options.format !== "markdown") {
|
|
29914
|
-
throw new ConfigValidationError(
|
|
29915
|
-
"VALIDATION_ERROR" /* ValidationError */,
|
|
29916
|
-
`Invalid format '${options.format}'. Must be 'json' or 'markdown'.`
|
|
30081
|
+
};
|
|
30082
|
+
var ExtractionProject = class {
|
|
30083
|
+
constructor(configDir, moduleContexts, resolvedConfig, repositoryName, draftComponents = []) {
|
|
30084
|
+
this.configDir = configDir;
|
|
30085
|
+
this.moduleContexts = moduleContexts;
|
|
30086
|
+
this.resolvedConfig = resolvedConfig;
|
|
30087
|
+
this.repositoryName = repositoryName;
|
|
30088
|
+
this.draftComponents = draftComponents;
|
|
30089
|
+
}
|
|
30090
|
+
extractDraftComponents(options) {
|
|
30091
|
+
const draftComponents = this.moduleContexts.flatMap(
|
|
30092
|
+
(moduleContext) => extractComponents(
|
|
30093
|
+
moduleContext.project,
|
|
30094
|
+
moduleContext.files,
|
|
30095
|
+
this.resolvedConfig,
|
|
30096
|
+
matchesGlob,
|
|
30097
|
+
this.configDir
|
|
30098
|
+
)
|
|
29917
30099
|
);
|
|
30100
|
+
if (!options.includeConnections) {
|
|
30101
|
+
return {
|
|
30102
|
+
kind: "draftOnly",
|
|
30103
|
+
components: draftComponents
|
|
30104
|
+
};
|
|
30105
|
+
}
|
|
30106
|
+
const enrichment = this.enrichDraftComponentValues(draftComponents, options.allowIncomplete);
|
|
30107
|
+
if (enrichment.kind === "fieldFailure") {
|
|
30108
|
+
return enrichment;
|
|
30109
|
+
}
|
|
30110
|
+
const connectionResult = this.detectConnections(enrichment.components, options.allowIncomplete);
|
|
30111
|
+
return {
|
|
30112
|
+
kind: "full",
|
|
30113
|
+
components: enrichment.components,
|
|
30114
|
+
failedFields: enrichment.failedFields,
|
|
30115
|
+
links: connectionResult.links,
|
|
30116
|
+
timings: connectionResult.timings
|
|
30117
|
+
};
|
|
29918
30118
|
}
|
|
29919
|
-
|
|
29920
|
-
|
|
29921
|
-
|
|
29922
|
-
|
|
30119
|
+
enrichDraftComponents(options) {
|
|
30120
|
+
if (!options.includeConnections) {
|
|
30121
|
+
return {
|
|
30122
|
+
kind: "draftOnly",
|
|
30123
|
+
components: this.draftComponents
|
|
30124
|
+
};
|
|
30125
|
+
}
|
|
30126
|
+
const enrichment = this.enrichDraftComponentValues(
|
|
30127
|
+
this.draftComponents,
|
|
30128
|
+
options.allowIncomplete
|
|
29923
30129
|
);
|
|
30130
|
+
if (enrichment.kind === "fieldFailure") {
|
|
30131
|
+
return enrichment;
|
|
30132
|
+
}
|
|
30133
|
+
const connectionResult = this.detectConnections(enrichment.components, options.allowIncomplete);
|
|
30134
|
+
return {
|
|
30135
|
+
kind: "full",
|
|
30136
|
+
components: enrichment.components,
|
|
30137
|
+
failedFields: enrichment.failedFields,
|
|
30138
|
+
links: connectionResult.links,
|
|
30139
|
+
timings: connectionResult.timings
|
|
30140
|
+
};
|
|
29924
30141
|
}
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
validateMutualExclusions(options);
|
|
29928
|
-
if (options.base !== void 0 && !options.pr) {
|
|
29929
|
-
throw new ConfigValidationError(
|
|
29930
|
-
"VALIDATION_ERROR" /* ValidationError */,
|
|
29931
|
-
"--base can only be used with --pr"
|
|
29932
|
-
);
|
|
30142
|
+
get moduleContextProjectNames() {
|
|
30143
|
+
return this.moduleContexts.map((moduleContext) => moduleContext.module.name);
|
|
29933
30144
|
}
|
|
29934
|
-
|
|
29935
|
-
|
|
29936
|
-
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
29941
|
-
|
|
29942
|
-
|
|
29943
|
-
constructor(message) {
|
|
29944
|
-
super(message);
|
|
29945
|
-
this.name = "DraftComponentLoadError";
|
|
29946
|
-
Error.captureStackTrace?.(this, this.constructor);
|
|
29947
|
-
}
|
|
29948
|
-
};
|
|
29949
|
-
function isDraftComponentArray(value) {
|
|
29950
|
-
if (!Array.isArray(value)) return false;
|
|
29951
|
-
return value.every(
|
|
29952
|
-
(item) => typeof item === "object" && item !== null && "type" in item && "name" in item && "domain" in item && "location" in item
|
|
29953
|
-
);
|
|
29954
|
-
}
|
|
29955
|
-
function parseJsonFile(filePath) {
|
|
29956
|
-
try {
|
|
29957
|
-
return JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
29958
|
-
} catch {
|
|
29959
|
-
throw new DraftComponentLoadError(`Enrich file contains invalid JSON: ${filePath}`);
|
|
29960
|
-
}
|
|
29961
|
-
}
|
|
29962
|
-
function loadDraftComponentsFromFile(filePath) {
|
|
29963
|
-
if (!existsSync4(filePath)) {
|
|
29964
|
-
throw new DraftComponentLoadError(`Enrich file not found: ${filePath}`);
|
|
29965
|
-
}
|
|
29966
|
-
const parsed = parseJsonFile(filePath);
|
|
29967
|
-
if (!isDraftComponentArray(parsed)) {
|
|
29968
|
-
throw new DraftComponentLoadError(
|
|
29969
|
-
`Enrich file does not contain valid draft components: ${filePath}`
|
|
29970
|
-
);
|
|
29971
|
-
}
|
|
29972
|
-
return parsed;
|
|
29973
|
-
}
|
|
29974
|
-
|
|
29975
|
-
// src/platform/infra/git/git-repository-info.ts
|
|
29976
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
29977
|
-
var RepositoryUrlParseError = class extends Error {
|
|
29978
|
-
/* v8 ignore start -- @preserve: Error constructor; tested via integration */
|
|
29979
|
-
constructor(url2) {
|
|
29980
|
-
super(`Expected owner and repo in git URL, got ${url2}`);
|
|
29981
|
-
this.name = "RepositoryUrlParseError";
|
|
29982
|
-
}
|
|
29983
|
-
/* v8 ignore stop */
|
|
29984
|
-
};
|
|
29985
|
-
function defaultGitExecutor2(binary, args, cwd) {
|
|
29986
|
-
return execFileSync2(binary, args, {
|
|
29987
|
-
cwd,
|
|
29988
|
-
encoding: "utf-8",
|
|
29989
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
29990
|
-
}).trim();
|
|
29991
|
-
}
|
|
29992
|
-
function runGit2(executor, gitBinary, cwd, args) {
|
|
29993
|
-
try {
|
|
29994
|
-
return executor(gitBinary, args, cwd);
|
|
29995
|
-
} catch (error48) {
|
|
29996
|
-
if (error48 instanceof Error && "code" in error48 && error48.code === "ENOENT") {
|
|
29997
|
-
throw new GitError("GIT_NOT_FOUND", "Install git to detect repository information.");
|
|
29998
|
-
}
|
|
29999
|
-
if (error48 instanceof Error) {
|
|
30000
|
-
const stderr = extractStderr(error48);
|
|
30001
|
-
if (stderr.includes("not a git repository")) {
|
|
30002
|
-
throw new GitError("NOT_A_REPOSITORY", "Run from within a git repository.");
|
|
30145
|
+
detectConnections(enrichedComponents, allowIncomplete) {
|
|
30146
|
+
const links = [];
|
|
30147
|
+
const timings = [];
|
|
30148
|
+
for (const moduleContext of this.moduleContexts) {
|
|
30149
|
+
const moduleComponents = enrichedComponents.filter(
|
|
30150
|
+
(component) => component.domain === moduleContext.module.name
|
|
30151
|
+
);
|
|
30152
|
+
if (moduleComponents.length === 0) {
|
|
30153
|
+
continue;
|
|
30003
30154
|
}
|
|
30155
|
+
const result = detectPerModuleConnections(
|
|
30156
|
+
moduleContext.project,
|
|
30157
|
+
moduleComponents,
|
|
30158
|
+
{
|
|
30159
|
+
allowIncomplete,
|
|
30160
|
+
moduleGlobs: [posix4.join(moduleContext.module.path, moduleContext.module.glob)],
|
|
30161
|
+
repository: this.repositoryName
|
|
30162
|
+
},
|
|
30163
|
+
matchesGlob
|
|
30164
|
+
);
|
|
30165
|
+
links.push(...result.links);
|
|
30166
|
+
timings.push({
|
|
30167
|
+
callGraphMs: result.timings.callGraphMs,
|
|
30168
|
+
asyncDetectionMs: 0,
|
|
30169
|
+
configurableMs: result.timings.configurableMs,
|
|
30170
|
+
setupMs: result.timings.setupMs,
|
|
30171
|
+
totalMs: result.timings.callGraphMs + result.timings.configurableMs + result.timings.setupMs
|
|
30172
|
+
});
|
|
30004
30173
|
}
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
|
|
30174
|
+
const crossResult = detectCrossModuleConnections(enrichedComponents, {
|
|
30175
|
+
allowIncomplete,
|
|
30176
|
+
repository: this.repositoryName
|
|
30177
|
+
});
|
|
30178
|
+
links.push(...crossResult.links);
|
|
30179
|
+
timings.push({
|
|
30180
|
+
callGraphMs: 0,
|
|
30181
|
+
asyncDetectionMs: crossResult.timings.asyncDetectionMs,
|
|
30182
|
+
configurableMs: 0,
|
|
30183
|
+
setupMs: 0,
|
|
30184
|
+
totalMs: crossResult.timings.asyncDetectionMs
|
|
30185
|
+
});
|
|
30016
30186
|
return {
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
url: url2
|
|
30187
|
+
links: deduplicateCrossStrategy(links),
|
|
30188
|
+
timings
|
|
30020
30189
|
};
|
|
30021
30190
|
}
|
|
30022
|
-
|
|
30023
|
-
|
|
30024
|
-
|
|
30025
|
-
|
|
30026
|
-
|
|
30027
|
-
|
|
30191
|
+
enrichDraftComponentValues(draftComponents, allowIncomplete) {
|
|
30192
|
+
const moduleNames = new Set(this.moduleContextProjectNames);
|
|
30193
|
+
const draftsByModule = groupDraftsByModule(draftComponents);
|
|
30194
|
+
assertAllDraftsMatchModules(draftsByModule, moduleNames);
|
|
30195
|
+
const components = [];
|
|
30196
|
+
const failedFieldSet = /* @__PURE__ */ new Set();
|
|
30197
|
+
for (const moduleContext of this.moduleContexts) {
|
|
30198
|
+
const moduleDrafts = draftsByModule.get(moduleContext.module.name) ?? [];
|
|
30199
|
+
if (moduleDrafts.length === 0) {
|
|
30200
|
+
continue;
|
|
30201
|
+
}
|
|
30202
|
+
const result = enrichComponents(
|
|
30203
|
+
moduleDrafts,
|
|
30204
|
+
this.resolvedConfig,
|
|
30205
|
+
moduleContext.project,
|
|
30206
|
+
matchesGlob,
|
|
30207
|
+
this.configDir
|
|
30208
|
+
);
|
|
30209
|
+
components.push(...result.components);
|
|
30210
|
+
for (const failure of result.failures) {
|
|
30211
|
+
failedFieldSet.add(failure.field);
|
|
30212
|
+
}
|
|
30213
|
+
}
|
|
30214
|
+
const failedFields = [...failedFieldSet];
|
|
30215
|
+
if (failedFields.length > 0 && !allowIncomplete) {
|
|
30216
|
+
return {
|
|
30217
|
+
kind: "fieldFailure",
|
|
30218
|
+
failedFields
|
|
30219
|
+
};
|
|
30028
30220
|
}
|
|
30029
30221
|
return {
|
|
30030
|
-
|
|
30031
|
-
|
|
30032
|
-
|
|
30222
|
+
kind: "enriched",
|
|
30223
|
+
components,
|
|
30224
|
+
failedFields
|
|
30033
30225
|
};
|
|
30034
30226
|
}
|
|
30035
|
-
|
|
30036
|
-
|
|
30037
|
-
|
|
30038
|
-
|
|
30227
|
+
};
|
|
30228
|
+
function assertAllDraftsMatchModules(draftsByModule, moduleNames) {
|
|
30229
|
+
const orphanedModules = [...draftsByModule.keys()].filter((name) => !moduleNames.has(name));
|
|
30230
|
+
if (orphanedModules.length > 0) {
|
|
30231
|
+
throw new OrphanedDraftComponentError(orphanedModules, [...moduleNames]);
|
|
30232
|
+
}
|
|
30039
30233
|
}
|
|
30040
|
-
function
|
|
30041
|
-
|
|
30042
|
-
|
|
30043
|
-
|
|
30044
|
-
|
|
30045
|
-
|
|
30046
|
-
|
|
30047
|
-
const stderr = extractStderr(error48);
|
|
30048
|
-
if (stderr.includes("No such remote")) {
|
|
30049
|
-
throw new GitError("NO_REMOTE", 'No git remote named "origin" found.');
|
|
30050
|
-
}
|
|
30234
|
+
function groupDraftsByModule(drafts) {
|
|
30235
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
30236
|
+
for (const draft of drafts) {
|
|
30237
|
+
const existing = grouped.get(draft.domain);
|
|
30238
|
+
if (existing !== void 0) {
|
|
30239
|
+
existing.push(draft);
|
|
30240
|
+
continue;
|
|
30051
30241
|
}
|
|
30052
|
-
|
|
30242
|
+
grouped.set(draft.domain, [draft]);
|
|
30053
30243
|
}
|
|
30244
|
+
return grouped;
|
|
30054
30245
|
}
|
|
30055
30246
|
|
|
30056
|
-
// src/features/extract/infra/external-clients/create-
|
|
30057
|
-
import {
|
|
30058
|
-
posix as posix4,
|
|
30059
|
-
resolve as resolve7
|
|
30060
|
-
} from "node:path";
|
|
30061
|
-
import { globSync as globSync2 } from "glob";
|
|
30062
|
-
|
|
30063
|
-
// src/features/extract/infra/external-clients/find-module-tsconfig-dir.ts
|
|
30247
|
+
// src/features/extract/infra/external-clients/create-configured-project.ts
|
|
30064
30248
|
import { existsSync as existsSync5 } from "node:fs";
|
|
30065
30249
|
import { resolve as resolve5 } from "node:path";
|
|
30066
|
-
function findModuleTsConfigDir(configDir, modulePath) {
|
|
30067
|
-
const moduleTsConfigPath = resolve5(configDir, modulePath, "tsconfig.json");
|
|
30068
|
-
if (existsSync5(moduleTsConfigPath)) {
|
|
30069
|
-
return resolve5(configDir, modulePath);
|
|
30070
|
-
}
|
|
30071
|
-
return configDir;
|
|
30072
|
-
}
|
|
30073
|
-
|
|
30074
|
-
// src/features/extract/infra/external-clients/create-configured-project.ts
|
|
30075
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
30076
|
-
import { resolve as resolve6 } from "node:path";
|
|
30077
30250
|
import { Project as Project2 } from "ts-morph";
|
|
30078
30251
|
function createConfiguredProject(configDir, skipTsConfig) {
|
|
30079
30252
|
if (skipTsConfig) {
|
|
30080
30253
|
return new Project2();
|
|
30081
30254
|
}
|
|
30082
|
-
const tsConfigPath =
|
|
30083
|
-
if (!
|
|
30255
|
+
const tsConfigPath = resolve5(configDir, "tsconfig.json");
|
|
30256
|
+
if (!existsSync5(tsConfigPath)) {
|
|
30084
30257
|
return new Project2();
|
|
30085
30258
|
}
|
|
30086
30259
|
return new Project2({
|
|
@@ -30089,200 +30262,238 @@ function createConfiguredProject(configDir, skipTsConfig) {
|
|
|
30089
30262
|
});
|
|
30090
30263
|
}
|
|
30091
30264
|
|
|
30092
|
-
// src/features/extract/infra/external-clients/
|
|
30093
|
-
|
|
30094
|
-
|
|
30095
|
-
|
|
30096
|
-
|
|
30265
|
+
// src/features/extract/infra/external-clients/find-module-tsconfig-dir.ts
|
|
30266
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
30267
|
+
import { resolve as resolve6 } from "node:path";
|
|
30268
|
+
function findModuleTsConfigDir(configDir, modulePath) {
|
|
30269
|
+
const moduleTsConfigPath = resolve6(configDir, modulePath, "tsconfig.json");
|
|
30270
|
+
if (existsSync6(moduleTsConfigPath)) {
|
|
30271
|
+
return resolve6(configDir, modulePath);
|
|
30272
|
+
}
|
|
30273
|
+
return configDir;
|
|
30097
30274
|
}
|
|
30098
30275
|
|
|
30099
|
-
// src/features/extract/infra/
|
|
30100
|
-
|
|
30101
|
-
|
|
30102
|
-
|
|
30103
|
-
const
|
|
30104
|
-
(
|
|
30276
|
+
// src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts
|
|
30277
|
+
var ExtractionProjectRepository = class {
|
|
30278
|
+
loadFromChangedProject(loadChangedProjectParams) {
|
|
30279
|
+
const parsedConfigState = this.loadParsedConfigState(loadChangedProjectParams.configPath);
|
|
30280
|
+
const sourceFilePaths = resolveFilteredSourceFiles(
|
|
30281
|
+
this.resolveSourceFilePaths(parsedConfigState),
|
|
30282
|
+
loadChangedProjectParams.baseBranch === void 0 ? { pr: true } : {
|
|
30283
|
+
base: loadChangedProjectParams.baseBranch,
|
|
30284
|
+
pr: true
|
|
30285
|
+
}
|
|
30105
30286
|
);
|
|
30106
|
-
|
|
30107
|
-
|
|
30108
|
-
|
|
30109
|
-
|
|
30110
|
-
|
|
30111
|
-
|
|
30112
|
-
|
|
30113
|
-
|
|
30287
|
+
return this.createExtractionProject(
|
|
30288
|
+
parsedConfigState,
|
|
30289
|
+
sourceFilePaths,
|
|
30290
|
+
loadChangedProjectParams.useTsConfig
|
|
30291
|
+
);
|
|
30292
|
+
}
|
|
30293
|
+
loadFromDraftEnrichment(draftEnrichmentParams) {
|
|
30294
|
+
const parsedConfigState = this.loadParsedConfigState(draftEnrichmentParams.configPath);
|
|
30295
|
+
return this.createExtractionProject(
|
|
30296
|
+
parsedConfigState,
|
|
30297
|
+
this.resolveSourceFilePaths(parsedConfigState),
|
|
30298
|
+
draftEnrichmentParams.useTsConfig,
|
|
30299
|
+
loadDraftComponentsFromFile(draftEnrichmentParams.draftComponentsPath)
|
|
30300
|
+
);
|
|
30301
|
+
}
|
|
30302
|
+
loadFromFullProject(loadFullProjectParams) {
|
|
30303
|
+
const parsedConfigState = this.loadParsedConfigState(loadFullProjectParams.configPath);
|
|
30304
|
+
return this.createExtractionProject(
|
|
30305
|
+
parsedConfigState,
|
|
30306
|
+
this.resolveSourceFilePaths(parsedConfigState),
|
|
30307
|
+
loadFullProjectParams.useTsConfig
|
|
30308
|
+
);
|
|
30309
|
+
}
|
|
30310
|
+
loadFromSelectedFiles(selectedFilesProjectParams) {
|
|
30311
|
+
const parsedConfigState = this.loadParsedConfigState(selectedFilesProjectParams.configPath);
|
|
30312
|
+
const sourceFilePaths = resolveFilteredSourceFiles(
|
|
30313
|
+
this.resolveSourceFilePaths(parsedConfigState),
|
|
30314
|
+
{ files: selectedFilesProjectParams.filePaths }
|
|
30315
|
+
);
|
|
30316
|
+
return this.createExtractionProject(
|
|
30317
|
+
parsedConfigState,
|
|
30318
|
+
sourceFilePaths,
|
|
30319
|
+
selectedFilesProjectParams.useTsConfig
|
|
30320
|
+
);
|
|
30321
|
+
}
|
|
30322
|
+
createExtractionProject(parsedConfigState, sourceFilePaths, useTsConfig, draftComponents = []) {
|
|
30323
|
+
return new ExtractionProject(
|
|
30324
|
+
parsedConfigState.configDir,
|
|
30325
|
+
this.createModuleContexts(parsedConfigState, sourceFilePaths, useTsConfig),
|
|
30326
|
+
parsedConfigState.resolvedConfig,
|
|
30327
|
+
getRepositoryInfo().name,
|
|
30328
|
+
draftComponents
|
|
30329
|
+
);
|
|
30330
|
+
}
|
|
30331
|
+
createModuleContexts(parsedConfigState, sourceFilePaths, useTsConfig) {
|
|
30332
|
+
const sourceFileSet = new Set(sourceFilePaths);
|
|
30333
|
+
return parsedConfigState.resolvedConfig.modules.map((module) => {
|
|
30334
|
+
const allModuleFiles = globSync2(posix5.join(module.path, module.glob), { cwd: parsedConfigState.configDir }).map((filePath) => resolve7(parsedConfigState.configDir, filePath));
|
|
30335
|
+
const moduleFiles = allModuleFiles.filter((filePath) => sourceFileSet.has(filePath));
|
|
30336
|
+
const moduleConfigDir = findModuleTsConfigDir(parsedConfigState.configDir, module.path);
|
|
30337
|
+
const project = createConfiguredProject(moduleConfigDir, !useTsConfig);
|
|
30338
|
+
project.addSourceFilesAtPaths(moduleFiles);
|
|
30339
|
+
return {
|
|
30340
|
+
files: moduleFiles,
|
|
30341
|
+
module,
|
|
30342
|
+
project
|
|
30343
|
+
};
|
|
30344
|
+
});
|
|
30345
|
+
}
|
|
30346
|
+
loadParsedConfigState(configPath) {
|
|
30347
|
+
return loadAndValidateConfig(configPath);
|
|
30348
|
+
}
|
|
30349
|
+
resolveSourceFilePaths(parsedConfigState) {
|
|
30350
|
+
return resolveSourceFiles(parsedConfigState.resolvedConfig, parsedConfigState.configDir);
|
|
30351
|
+
}
|
|
30352
|
+
};
|
|
30353
|
+
|
|
30354
|
+
// src/features/extract/commands/enrich-draft-components.ts
|
|
30355
|
+
function enrichDraftComponents(enrichDraftComponentsInput) {
|
|
30356
|
+
const extractionProjectRepository = new ExtractionProjectRepository();
|
|
30357
|
+
const extractionProject = extractionProjectRepository.loadFromDraftEnrichment({
|
|
30358
|
+
configPath: enrichDraftComponentsInput.configPath,
|
|
30359
|
+
draftComponentsPath: enrichDraftComponentsInput.draftComponentsPath,
|
|
30360
|
+
useTsConfig: enrichDraftComponentsInput.useTsConfig
|
|
30361
|
+
});
|
|
30362
|
+
return extractionProject.enrichDraftComponents({
|
|
30363
|
+
allowIncomplete: enrichDraftComponentsInput.allowIncomplete,
|
|
30364
|
+
includeConnections: enrichDraftComponentsInput.includeConnections
|
|
30114
30365
|
});
|
|
30115
30366
|
}
|
|
30116
30367
|
|
|
30117
|
-
// src/features/extract/
|
|
30118
|
-
function extractDraftComponents(
|
|
30119
|
-
|
|
30120
|
-
|
|
30368
|
+
// src/features/extract/commands/extract-draft-components.ts
|
|
30369
|
+
function extractDraftComponents(extractDraftComponentsInput) {
|
|
30370
|
+
const extractionProjectRepository = new ExtractionProjectRepository();
|
|
30371
|
+
const extractionProject = loadProjectFromInput(
|
|
30372
|
+
extractionProjectRepository,
|
|
30373
|
+
extractDraftComponentsInput
|
|
30121
30374
|
);
|
|
30375
|
+
return extractionProject.extractDraftComponents({
|
|
30376
|
+
allowIncomplete: extractDraftComponentsInput.allowIncomplete,
|
|
30377
|
+
includeConnections: extractDraftComponentsInput.includeConnections
|
|
30378
|
+
});
|
|
30122
30379
|
}
|
|
30123
|
-
|
|
30124
|
-
|
|
30125
|
-
|
|
30126
|
-
|
|
30127
|
-
|
|
30128
|
-
|
|
30129
|
-
);
|
|
30130
|
-
this.name = "OrphanedDraftComponentError";
|
|
30380
|
+
function loadProjectFromInput(extractionProjectRepository, extractDraftComponentsInput) {
|
|
30381
|
+
if (extractDraftComponentsInput.sourceMode === "pull-request") {
|
|
30382
|
+
return extractionProjectRepository.loadFromChangedProject({
|
|
30383
|
+
configPath: extractDraftComponentsInput.configPath,
|
|
30384
|
+
...extractDraftComponentsInput.baseBranch === void 0 ? {} : { baseBranch: extractDraftComponentsInput.baseBranch },
|
|
30385
|
+
useTsConfig: extractDraftComponentsInput.useTsConfig
|
|
30386
|
+
});
|
|
30131
30387
|
}
|
|
30132
|
-
|
|
30133
|
-
|
|
30134
|
-
|
|
30135
|
-
|
|
30136
|
-
|
|
30137
|
-
|
|
30138
|
-
const failedFieldSet = /* @__PURE__ */ new Set();
|
|
30139
|
-
for (const ctx of moduleContexts) {
|
|
30140
|
-
const moduleDrafts = draftsByModule.get(ctx.module.name) ?? [];
|
|
30141
|
-
if (moduleDrafts.length === 0) {
|
|
30142
|
-
continue;
|
|
30143
|
-
}
|
|
30144
|
-
const result = enrichComponents(
|
|
30145
|
-
moduleDrafts,
|
|
30146
|
-
resolvedConfig,
|
|
30147
|
-
ctx.project,
|
|
30148
|
-
matchesGlob,
|
|
30149
|
-
configDir
|
|
30150
|
-
);
|
|
30151
|
-
components.push(...result.components);
|
|
30152
|
-
for (const f of result.failures) {
|
|
30153
|
-
failedFieldSet.add(f.field);
|
|
30154
|
-
}
|
|
30388
|
+
if (extractDraftComponentsInput.sourceMode === "files") {
|
|
30389
|
+
return extractionProjectRepository.loadFromSelectedFiles({
|
|
30390
|
+
configPath: extractDraftComponentsInput.configPath,
|
|
30391
|
+
filePaths: extractDraftComponentsInput.files ?? [],
|
|
30392
|
+
useTsConfig: extractDraftComponentsInput.useTsConfig
|
|
30393
|
+
});
|
|
30155
30394
|
}
|
|
30395
|
+
return extractionProjectRepository.loadFromFullProject({
|
|
30396
|
+
configPath: extractDraftComponentsInput.configPath,
|
|
30397
|
+
useTsConfig: extractDraftComponentsInput.useTsConfig
|
|
30398
|
+
});
|
|
30399
|
+
}
|
|
30400
|
+
|
|
30401
|
+
// src/features/extract/commands/create-extract-draft-components-input.ts
|
|
30402
|
+
function createExtractDraftComponentsInput(options) {
|
|
30156
30403
|
return {
|
|
30157
|
-
|
|
30158
|
-
|
|
30404
|
+
allowIncomplete: options.allowIncomplete === true,
|
|
30405
|
+
...options.base === void 0 ? {} : { baseBranch: options.base },
|
|
30406
|
+
configPath: options.config,
|
|
30407
|
+
...options.files === void 0 ? {} : { files: options.files },
|
|
30408
|
+
includeConnections: !shouldStopAtDraftComponents(options),
|
|
30409
|
+
...options.output === void 0 ? {} : { output: options.output },
|
|
30410
|
+
sourceMode: readSourceMode(options),
|
|
30411
|
+
useTsConfig: options.tsConfig !== false
|
|
30159
30412
|
};
|
|
30160
30413
|
}
|
|
30161
|
-
function
|
|
30162
|
-
|
|
30163
|
-
if (orphanedModules.length > 0) {
|
|
30164
|
-
throw new OrphanedDraftComponentError(orphanedModules, [...moduleNames]);
|
|
30165
|
-
}
|
|
30414
|
+
function shouldStopAtDraftComponents(options) {
|
|
30415
|
+
return options.dryRun === true || options.format === "markdown" || options.componentsOnly === true;
|
|
30166
30416
|
}
|
|
30167
|
-
function
|
|
30168
|
-
|
|
30169
|
-
|
|
30170
|
-
const existing = grouped.get(draft.domain);
|
|
30171
|
-
if (existing) {
|
|
30172
|
-
existing.push(draft);
|
|
30173
|
-
} else {
|
|
30174
|
-
grouped.set(draft.domain, [draft]);
|
|
30175
|
-
}
|
|
30417
|
+
function readSourceMode(options) {
|
|
30418
|
+
if (options.pr === true) {
|
|
30419
|
+
return "pull-request";
|
|
30176
30420
|
}
|
|
30177
|
-
return
|
|
30421
|
+
return options.files === void 0 ? "all" : "files";
|
|
30178
30422
|
}
|
|
30179
30423
|
|
|
30180
|
-
// src/features/extract/
|
|
30181
|
-
|
|
30182
|
-
function detectConnectionsPerModule(moduleContexts, enrichedComponents, repositoryName, allowIncomplete) {
|
|
30183
|
-
const links = [];
|
|
30184
|
-
const timings = [];
|
|
30185
|
-
for (const ctx of moduleContexts) {
|
|
30186
|
-
const moduleComponents = enrichedComponents.filter((c) => c.domain === ctx.module.name);
|
|
30187
|
-
if (moduleComponents.length === 0) {
|
|
30188
|
-
continue;
|
|
30189
|
-
}
|
|
30190
|
-
const result = detectPerModuleConnections(
|
|
30191
|
-
ctx.project,
|
|
30192
|
-
moduleComponents,
|
|
30193
|
-
{
|
|
30194
|
-
allowIncomplete,
|
|
30195
|
-
moduleGlobs: [posix5.join(ctx.module.path, ctx.module.glob)],
|
|
30196
|
-
repository: repositoryName
|
|
30197
|
-
},
|
|
30198
|
-
matchesGlob
|
|
30199
|
-
);
|
|
30200
|
-
links.push(...result.links);
|
|
30201
|
-
timings.push({
|
|
30202
|
-
callGraphMs: result.timings.callGraphMs,
|
|
30203
|
-
asyncDetectionMs: 0,
|
|
30204
|
-
configurableMs: result.timings.configurableMs,
|
|
30205
|
-
setupMs: result.timings.setupMs,
|
|
30206
|
-
totalMs: result.timings.callGraphMs + result.timings.configurableMs + result.timings.setupMs
|
|
30207
|
-
});
|
|
30208
|
-
}
|
|
30209
|
-
const crossResult = detectCrossModuleConnections(enrichedComponents, {
|
|
30210
|
-
allowIncomplete,
|
|
30211
|
-
repository: repositoryName
|
|
30212
|
-
});
|
|
30213
|
-
links.push(...crossResult.links);
|
|
30214
|
-
timings.push({
|
|
30215
|
-
callGraphMs: 0,
|
|
30216
|
-
asyncDetectionMs: crossResult.timings.asyncDetectionMs,
|
|
30217
|
-
configurableMs: 0,
|
|
30218
|
-
setupMs: 0,
|
|
30219
|
-
totalMs: crossResult.timings.asyncDetectionMs
|
|
30220
|
-
});
|
|
30424
|
+
// src/features/extract/commands/create-enrich-draft-components-input.ts
|
|
30425
|
+
function createEnrichDraftComponentsInput(options, enrichPath) {
|
|
30221
30426
|
return {
|
|
30222
|
-
|
|
30223
|
-
|
|
30427
|
+
allowIncomplete: options.allowIncomplete === true,
|
|
30428
|
+
configPath: options.config,
|
|
30429
|
+
draftComponentsPath: enrichPath,
|
|
30430
|
+
includeConnections: !shouldStopAtDraftComponents2(options),
|
|
30431
|
+
...options.output === void 0 ? {} : { output: options.output },
|
|
30432
|
+
useTsConfig: options.tsConfig !== false
|
|
30224
30433
|
};
|
|
30225
30434
|
}
|
|
30435
|
+
function shouldStopAtDraftComponents2(options) {
|
|
30436
|
+
return options.dryRun === true || options.format === "markdown" || options.componentsOnly === true;
|
|
30437
|
+
}
|
|
30226
30438
|
|
|
30227
|
-
// src/
|
|
30228
|
-
function
|
|
30229
|
-
|
|
30230
|
-
|
|
30231
|
-
|
|
30232
|
-
|
|
30233
|
-
|
|
30234
|
-
|
|
30235
|
-
|
|
30236
|
-
|
|
30237
|
-
|
|
30439
|
+
// src/platform/infra/cli-presentation/categorize-components.ts
|
|
30440
|
+
function componentKey(component) {
|
|
30441
|
+
return `${component.domain}:${component.type}:${component.name}`;
|
|
30442
|
+
}
|
|
30443
|
+
function toComponentSummary(component) {
|
|
30444
|
+
return {
|
|
30445
|
+
type: component.type,
|
|
30446
|
+
name: component.name,
|
|
30447
|
+
domain: component.domain
|
|
30448
|
+
};
|
|
30449
|
+
}
|
|
30450
|
+
function categorizeComponents(current, baseline) {
|
|
30451
|
+
if (baseline === void 0) {
|
|
30238
30452
|
return {
|
|
30239
|
-
|
|
30240
|
-
|
|
30453
|
+
added: current.map(toComponentSummary),
|
|
30454
|
+
modified: [],
|
|
30455
|
+
removed: []
|
|
30241
30456
|
};
|
|
30242
30457
|
}
|
|
30243
|
-
const
|
|
30244
|
-
const
|
|
30245
|
-
|
|
30246
|
-
|
|
30247
|
-
}
|
|
30248
|
-
const repositoryInfo = getRepositoryInfo();
|
|
30249
|
-
const connectionResult = detectConnectionsPerModule(
|
|
30250
|
-
moduleContexts,
|
|
30251
|
-
enrichment.components,
|
|
30252
|
-
repositoryInfo.name,
|
|
30253
|
-
allowIncomplete
|
|
30254
|
-
);
|
|
30458
|
+
const baselineKeys = new Set(baseline.map((c) => componentKey(c)));
|
|
30459
|
+
const currentKeys = new Set(current.map((c) => componentKey(c)));
|
|
30460
|
+
const added = current.filter((c) => !baselineKeys.has(componentKey(c))).map(toComponentSummary);
|
|
30461
|
+
const removed = baseline.filter((c) => !currentKeys.has(componentKey(c))).map(toComponentSummary);
|
|
30255
30462
|
return {
|
|
30256
|
-
|
|
30257
|
-
|
|
30258
|
-
|
|
30259
|
-
timings: connectionResult.timings,
|
|
30260
|
-
failedFields: enrichment.failedFields
|
|
30463
|
+
added,
|
|
30464
|
+
modified: [],
|
|
30465
|
+
removed
|
|
30261
30466
|
};
|
|
30262
30467
|
}
|
|
30263
30468
|
|
|
30264
|
-
// src/platform/infra/cli-presentation/format-
|
|
30265
|
-
function
|
|
30266
|
-
|
|
30469
|
+
// src/platform/infra/cli-presentation/format-extraction-stats.ts
|
|
30470
|
+
function countLinksByType(componentCount, links) {
|
|
30471
|
+
const syncLinkCount = links.filter((l) => l.type === "sync").length;
|
|
30472
|
+
const asyncLinkCount = links.filter((l) => l.type === "async").length;
|
|
30473
|
+
const uncertainLinkCount = links.filter((l) => l._uncertain !== void 0).length;
|
|
30474
|
+
return {
|
|
30475
|
+
componentCount,
|
|
30476
|
+
linkCount: links.length,
|
|
30477
|
+
syncLinkCount,
|
|
30478
|
+
asyncLinkCount,
|
|
30479
|
+
uncertainLinkCount
|
|
30480
|
+
};
|
|
30267
30481
|
}
|
|
30268
|
-
function
|
|
30269
|
-
|
|
30270
|
-
|
|
30271
|
-
|
|
30272
|
-
|
|
30482
|
+
function formatSeconds(ms) {
|
|
30483
|
+
return (ms / 1e3).toFixed(2) + "s";
|
|
30484
|
+
}
|
|
30485
|
+
function formatExtractionStats(stats) {
|
|
30486
|
+
const lines = [`Components: ${stats.componentCount}`];
|
|
30487
|
+
if (stats.linkCount !== void 0) {
|
|
30488
|
+
lines.push(
|
|
30489
|
+
`Links: ${stats.linkCount} (sync: ${stats.syncLinkCount}, async: ${stats.asyncLinkCount})`
|
|
30490
|
+
);
|
|
30491
|
+
lines.push(`Uncertain: ${stats.uncertainLinkCount}`);
|
|
30273
30492
|
}
|
|
30274
|
-
return
|
|
30275
|
-
${components.map(formatComponentLine).join("\n")}`;
|
|
30493
|
+
return lines;
|
|
30276
30494
|
}
|
|
30277
|
-
function
|
|
30278
|
-
|
|
30279
|
-
formatSection("Added Components", categorized.added),
|
|
30280
|
-
formatSection("Modified Components", categorized.modified),
|
|
30281
|
-
formatSection("Removed Components", categorized.removed)
|
|
30282
|
-
];
|
|
30283
|
-
return `## Architecture Changes
|
|
30284
|
-
|
|
30285
|
-
${sections.join("\n\n")}`;
|
|
30495
|
+
function formatTimingLine(timings) {
|
|
30496
|
+
return `Extraction completed in ${formatSeconds(timings.totalMs)} (call graph: ${formatSeconds(timings.callGraphMs)}, detection: ${formatSeconds(timings.asyncDetectionMs)}, setup: ${formatSeconds(timings.setupMs)})`;
|
|
30286
30497
|
}
|
|
30287
30498
|
|
|
30288
30499
|
// src/platform/infra/cli-presentation/extract-output-formatter.ts
|
|
@@ -30311,6 +30522,30 @@ function formatDryRunOutput(components) {
|
|
|
30311
30522
|
return lines;
|
|
30312
30523
|
}
|
|
30313
30524
|
|
|
30525
|
+
// src/platform/infra/cli-presentation/format-pr-markdown.ts
|
|
30526
|
+
function formatComponentLine(component) {
|
|
30527
|
+
return `- **${component.type}** \`${component.name}\` in \`${component.domain}\` domain`;
|
|
30528
|
+
}
|
|
30529
|
+
function formatSection(title, components) {
|
|
30530
|
+
const header = `### ${title} (${components.length})`;
|
|
30531
|
+
if (components.length === 0) {
|
|
30532
|
+
return `${header}
|
|
30533
|
+
None`;
|
|
30534
|
+
}
|
|
30535
|
+
return `${header}
|
|
30536
|
+
${components.map(formatComponentLine).join("\n")}`;
|
|
30537
|
+
}
|
|
30538
|
+
function formatPrMarkdown(categorized) {
|
|
30539
|
+
const sections = [
|
|
30540
|
+
formatSection("Added Components", categorized.added),
|
|
30541
|
+
formatSection("Modified Components", categorized.modified),
|
|
30542
|
+
formatSection("Removed Components", categorized.removed)
|
|
30543
|
+
];
|
|
30544
|
+
return `## Architecture Changes
|
|
30545
|
+
|
|
30546
|
+
${sections.join("\n\n")}`;
|
|
30547
|
+
}
|
|
30548
|
+
|
|
30314
30549
|
// src/platform/infra/cli-presentation/output-writer.ts
|
|
30315
30550
|
import { writeFileSync } from "node:fs";
|
|
30316
30551
|
function outputResult(data, options) {
|
|
@@ -30333,67 +30568,7 @@ function outputResult(data, options) {
|
|
|
30333
30568
|
console.log(JSON.stringify(data));
|
|
30334
30569
|
}
|
|
30335
30570
|
|
|
30336
|
-
// src/
|
|
30337
|
-
function countLinksByType(componentCount, links) {
|
|
30338
|
-
const syncLinkCount = links.filter((l) => l.type === "sync").length;
|
|
30339
|
-
const asyncLinkCount = links.filter((l) => l.type === "async").length;
|
|
30340
|
-
const uncertainLinkCount = links.filter((l) => l._uncertain !== void 0).length;
|
|
30341
|
-
return {
|
|
30342
|
-
componentCount,
|
|
30343
|
-
linkCount: links.length,
|
|
30344
|
-
syncLinkCount,
|
|
30345
|
-
asyncLinkCount,
|
|
30346
|
-
uncertainLinkCount
|
|
30347
|
-
};
|
|
30348
|
-
}
|
|
30349
|
-
function formatSeconds(ms) {
|
|
30350
|
-
return (ms / 1e3).toFixed(2) + "s";
|
|
30351
|
-
}
|
|
30352
|
-
function formatExtractionStats(stats) {
|
|
30353
|
-
const lines = [`Components: ${stats.componentCount}`];
|
|
30354
|
-
if (stats.linkCount !== void 0) {
|
|
30355
|
-
lines.push(
|
|
30356
|
-
`Links: ${stats.linkCount} (sync: ${stats.syncLinkCount}, async: ${stats.asyncLinkCount})`
|
|
30357
|
-
);
|
|
30358
|
-
lines.push(`Uncertain: ${stats.uncertainLinkCount}`);
|
|
30359
|
-
}
|
|
30360
|
-
return lines;
|
|
30361
|
-
}
|
|
30362
|
-
function formatTimingLine(timings) {
|
|
30363
|
-
return `Extraction completed in ${formatSeconds(timings.totalMs)} (call graph: ${formatSeconds(timings.callGraphMs)}, detection: ${formatSeconds(timings.asyncDetectionMs)}, setup: ${formatSeconds(timings.setupMs)})`;
|
|
30364
|
-
}
|
|
30365
|
-
|
|
30366
|
-
// src/platform/infra/cli-presentation/categorize-components.ts
|
|
30367
|
-
function componentKey(component) {
|
|
30368
|
-
return `${component.domain}:${component.type}:${component.name}`;
|
|
30369
|
-
}
|
|
30370
|
-
function toComponentSummary(component) {
|
|
30371
|
-
return {
|
|
30372
|
-
type: component.type,
|
|
30373
|
-
name: component.name,
|
|
30374
|
-
domain: component.domain
|
|
30375
|
-
};
|
|
30376
|
-
}
|
|
30377
|
-
function categorizeComponents(current, baseline) {
|
|
30378
|
-
if (baseline === void 0) {
|
|
30379
|
-
return {
|
|
30380
|
-
added: current.map(toComponentSummary),
|
|
30381
|
-
modified: [],
|
|
30382
|
-
removed: []
|
|
30383
|
-
};
|
|
30384
|
-
}
|
|
30385
|
-
const baselineKeys = new Set(baseline.map((c) => componentKey(c)));
|
|
30386
|
-
const currentKeys = new Set(current.map((c) => componentKey(c)));
|
|
30387
|
-
const added = current.filter((c) => !baselineKeys.has(componentKey(c))).map(toComponentSummary);
|
|
30388
|
-
const removed = baseline.filter((c) => !currentKeys.has(componentKey(c))).map(toComponentSummary);
|
|
30389
|
-
return {
|
|
30390
|
-
added,
|
|
30391
|
-
modified: [],
|
|
30392
|
-
removed
|
|
30393
|
-
};
|
|
30394
|
-
}
|
|
30395
|
-
|
|
30396
|
-
// src/features/extract/infra/mappers/present-extraction-result.ts
|
|
30571
|
+
// src/features/extract/infra/cli/output/present-extraction-result.ts
|
|
30397
30572
|
function presentExtractionResult(result, options) {
|
|
30398
30573
|
if (result.kind === "draftOnly") {
|
|
30399
30574
|
presentDraftResult(result.components, options);
|
|
@@ -30414,7 +30589,7 @@ function presentDraftResult(components, options) {
|
|
|
30414
30589
|
console.log(markdown);
|
|
30415
30590
|
return;
|
|
30416
30591
|
}
|
|
30417
|
-
outputResult(formatSuccess(components),
|
|
30592
|
+
outputResult(formatSuccess(components), createOutputOptions(options.output));
|
|
30418
30593
|
}
|
|
30419
30594
|
function presentFullResult(result, options) {
|
|
30420
30595
|
if (result.failedFields.length > 0) {
|
|
@@ -30436,21 +30611,25 @@ function presentFullResult(result, options) {
|
|
|
30436
30611
|
components: result.components,
|
|
30437
30612
|
links: result.links
|
|
30438
30613
|
}),
|
|
30439
|
-
|
|
30614
|
+
createOutputOptions(options.output)
|
|
30440
30615
|
);
|
|
30441
30616
|
}
|
|
30617
|
+
function createOutputOptions(outputPath) {
|
|
30618
|
+
return outputPath === void 0 ? {} : { output: outputPath };
|
|
30619
|
+
}
|
|
30442
30620
|
|
|
30443
30621
|
// src/features/extract/entrypoint/extract.ts
|
|
30444
30622
|
function createExtractCommand() {
|
|
30445
30623
|
return new Command21("extract").description("Extract architectural components from source code").requiredOption("--config <path>", "Path to extraction config file").option("--dry-run", "Show component counts per domain without full output").option("-o, --output <file>", "Write output to file instead of stdout").option("--components-only", "Output only component identity (no metadata enrichment)").option("--enrich <file>", "Read draft components from file and enrich with extraction rules").option("--allow-incomplete", "Output components even when some extraction fields fail").option("--pr", "Extract from files changed in current branch vs base branch").option("--base <branch>", "Override base branch for --pr (default: auto-detect)").option("--files <paths...>", "Extract from specific files").option("--format <type>", "Output format: json (default) or markdown").option("--stats", "Show extraction statistics on stderr").option("--patterns", "Enable pattern-based connection detection").option("--no-ts-config", "Skip tsconfig.json auto-discovery (disables full type resolution)").action((options) => {
|
|
30446
30624
|
validateFlagCombinations(options);
|
|
30447
|
-
const
|
|
30448
|
-
|
|
30449
|
-
|
|
30450
|
-
|
|
30451
|
-
|
|
30452
|
-
|
|
30453
|
-
|
|
30625
|
+
const result = options.enrich === void 0 ? extractDraftComponents(createExtractDraftComponentsInput(options)) : enrichDraftComponents(createEnrichDraftComponentsInput(options, options.enrich));
|
|
30626
|
+
if (result.kind === "fieldFailure") {
|
|
30627
|
+
exitWithCliError(
|
|
30628
|
+
"VALIDATION_ERROR" /* ValidationError */,
|
|
30629
|
+
`Extraction failed for fields: ${result.failedFields.join(", ")}`,
|
|
30630
|
+
1 /* ExtractionFailure */
|
|
30631
|
+
);
|
|
30632
|
+
}
|
|
30454
30633
|
presentExtractionResult(result, options);
|
|
30455
30634
|
});
|
|
30456
30635
|
}
|
|
@@ -30505,7 +30684,6 @@ export {
|
|
|
30505
30684
|
CliErrorCode,
|
|
30506
30685
|
ConfigValidationError,
|
|
30507
30686
|
ExitCode,
|
|
30508
|
-
ExtractionFieldFailureError,
|
|
30509
30687
|
createProgram,
|
|
30510
30688
|
formatError2 as formatError,
|
|
30511
30689
|
formatSuccess,
|
|
@@ -30528,9 +30706,9 @@ export {
|
|
|
30528
30706
|
/* v8 ignore next -- @preserve: yaml library always throws Error instances; defensive guard */
|
|
30529
30707
|
/* v8 ignore next -- @preserve: error is always Error from yaml parser; defensive guard */
|
|
30530
30708
|
/* v8 ignore start -- @preserve: default executor delegates to execFileSync; tested via CLI integration */
|
|
30531
|
-
/* v8 ignore
|
|
30532
|
-
/* v8 ignore start -- @preserve: git execution; mocked in all integration tests */
|
|
30709
|
+
/* v8 ignore next -- @preserve: defensive optional chain; property existence guaranteed by hasOwn check above */
|
|
30533
30710
|
/* v8 ignore start -- @preserve: defensive check; regex ([^/]+) requires non-empty groups */
|
|
30711
|
+
/* v8 ignore start -- @preserve: detectChangedTypeScriptFiles only throws GitError; non-GitError path is unreachable */
|
|
30534
30712
|
/* v8 ignore start -- @preserve: trivial comparator, Map keys guarantee a !== b */
|
|
30535
30713
|
/* v8 ignore start -- @preserve: dry-run output formatting; tested via CLI integration */
|
|
30536
30714
|
/* v8 ignore start -- @preserve: dry-run tested via CLI integration */
|