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