@archpilotlabs/archpilot 0.0.9 → 0.0.10
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/archpilot-cli.js +823 -70
- package/package.json +1 -1
package/dist/archpilot-cli.js
CHANGED
|
@@ -213475,7 +213475,264 @@ async function listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath =
|
|
|
213475
213475
|
const entries = await import_node_fs.promises.readdir(modulesRoot, { withFileTypes: true });
|
|
213476
213476
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
|
|
213477
213477
|
}
|
|
213478
|
+
var ignoredModuleDirectoryNames = /* @__PURE__ */ new Set([
|
|
213479
|
+
".git",
|
|
213480
|
+
".archpilot",
|
|
213481
|
+
".vscode",
|
|
213482
|
+
".vscode-test",
|
|
213483
|
+
"node_modules",
|
|
213484
|
+
"dist",
|
|
213485
|
+
"build",
|
|
213486
|
+
"out",
|
|
213487
|
+
"coverage",
|
|
213488
|
+
".next",
|
|
213489
|
+
"generated",
|
|
213490
|
+
"target",
|
|
213491
|
+
"vendor",
|
|
213492
|
+
".venv",
|
|
213493
|
+
"venv",
|
|
213494
|
+
"__pycache__"
|
|
213495
|
+
]);
|
|
213496
|
+
var noisyModuleDirectoryNames = /* @__PURE__ */ new Set([
|
|
213497
|
+
"components",
|
|
213498
|
+
"hooks",
|
|
213499
|
+
"utils",
|
|
213500
|
+
"util",
|
|
213501
|
+
"lib",
|
|
213502
|
+
"common",
|
|
213503
|
+
"shared",
|
|
213504
|
+
"shared-ui",
|
|
213505
|
+
"types",
|
|
213506
|
+
"constants",
|
|
213507
|
+
"config",
|
|
213508
|
+
"test",
|
|
213509
|
+
"tests",
|
|
213510
|
+
"__tests__",
|
|
213511
|
+
"fixtures",
|
|
213512
|
+
"mocks",
|
|
213513
|
+
"__mocks__",
|
|
213514
|
+
"generated",
|
|
213515
|
+
"dist",
|
|
213516
|
+
"build",
|
|
213517
|
+
"out",
|
|
213518
|
+
"node_modules"
|
|
213519
|
+
]);
|
|
213520
|
+
var sourceFileExtensions = /* @__PURE__ */ new Set([
|
|
213521
|
+
".ts",
|
|
213522
|
+
".tsx",
|
|
213523
|
+
".js",
|
|
213524
|
+
".jsx",
|
|
213525
|
+
".vue",
|
|
213526
|
+
".java",
|
|
213527
|
+
".py",
|
|
213528
|
+
".php",
|
|
213529
|
+
".go",
|
|
213530
|
+
".cs",
|
|
213531
|
+
".rb",
|
|
213532
|
+
".kt",
|
|
213533
|
+
".swift",
|
|
213534
|
+
".rs"
|
|
213535
|
+
]);
|
|
213536
|
+
var publicEntrypointNames = /* @__PURE__ */ new Set([
|
|
213537
|
+
"index.ts",
|
|
213538
|
+
"index.tsx",
|
|
213539
|
+
"index.js",
|
|
213540
|
+
"index.jsx",
|
|
213541
|
+
"index.vue",
|
|
213542
|
+
"__init__.py",
|
|
213543
|
+
"main.go",
|
|
213544
|
+
"mod.ts",
|
|
213545
|
+
"mod.js"
|
|
213546
|
+
]);
|
|
213547
|
+
function isIgnoredDirectoryName(directoryName) {
|
|
213548
|
+
return ignoredModuleDirectoryNames.has(directoryName.toLowerCase());
|
|
213549
|
+
}
|
|
213550
|
+
function isNoisyModuleName(moduleName) {
|
|
213551
|
+
return noisyModuleDirectoryNames.has(moduleName.toLowerCase());
|
|
213552
|
+
}
|
|
213553
|
+
function toPathSegments(relativePath) {
|
|
213554
|
+
return normalizePath(relativePath).split("/").filter((segment) => segment.length > 0);
|
|
213555
|
+
}
|
|
213556
|
+
function toModuleIdFromPath(relativePath, fallbackRoot) {
|
|
213557
|
+
const segments = toPathSegments(relativePath);
|
|
213558
|
+
const lowerSegments = segments.map((segment) => segment.toLowerCase());
|
|
213559
|
+
const sourceIndex = lowerSegments.lastIndexOf("src");
|
|
213560
|
+
if (sourceIndex >= 0 && sourceIndex + 2 < segments.length) {
|
|
213561
|
+
const parent = segments[sourceIndex - 1];
|
|
213562
|
+
const container = lowerSegments[sourceIndex + 1];
|
|
213563
|
+
if (["modules", "features"].includes(container)) {
|
|
213564
|
+
return [parent, segments[sourceIndex + 2]].filter(Boolean).join("/");
|
|
213565
|
+
}
|
|
213566
|
+
if (["domain", "domains", "application", "infrastructure"].includes(container)) {
|
|
213567
|
+
return [segments[sourceIndex + 1], segments[sourceIndex + 2]].join("/");
|
|
213568
|
+
}
|
|
213569
|
+
return [parent, segments[sourceIndex + 1]].filter(Boolean).join("/");
|
|
213570
|
+
}
|
|
213571
|
+
if (sourceIndex >= 0 && sourceIndex + 1 < segments.length) {
|
|
213572
|
+
const parent = segments[sourceIndex - 1];
|
|
213573
|
+
return [parent, segments[sourceIndex + 1]].filter(Boolean).join("/");
|
|
213574
|
+
}
|
|
213575
|
+
if (segments[0] === "src" && segments.length >= 3) {
|
|
213576
|
+
return `${segments[1]}/${segments[2]}`;
|
|
213577
|
+
}
|
|
213578
|
+
if (fallbackRoot) {
|
|
213579
|
+
const rootSegments = toPathSegments(fallbackRoot);
|
|
213580
|
+
const relativeSegments = segments.slice(rootSegments.length);
|
|
213581
|
+
if (relativeSegments.length > 0) {
|
|
213582
|
+
return relativeSegments.join("/");
|
|
213583
|
+
}
|
|
213584
|
+
}
|
|
213585
|
+
return segments.slice(-1)[0] ?? relativePath;
|
|
213586
|
+
}
|
|
213587
|
+
function isKnownNestedModuleRoot(relativePath) {
|
|
213588
|
+
const segments = toPathSegments(relativePath).map((segment) => segment.toLowerCase());
|
|
213589
|
+
if (segments.length < 2) {
|
|
213590
|
+
return false;
|
|
213591
|
+
}
|
|
213592
|
+
const lastParent = segments[segments.length - 2];
|
|
213593
|
+
if (["modules", "features", "domain", "domains", "application", "infrastructure"].includes(lastParent)) {
|
|
213594
|
+
return true;
|
|
213595
|
+
}
|
|
213596
|
+
if (segments.length >= 3 && segments[segments.length - 3] === "src") {
|
|
213597
|
+
return true;
|
|
213598
|
+
}
|
|
213599
|
+
return false;
|
|
213600
|
+
}
|
|
213601
|
+
async function inspectPotentialModuleRoot(absolutePath) {
|
|
213602
|
+
let sourceFileCount = 0;
|
|
213603
|
+
let hasPublicEntrypoint = false;
|
|
213604
|
+
const queue = [{ absolutePath, depth: 0 }];
|
|
213605
|
+
while (queue.length > 0) {
|
|
213606
|
+
const current = queue.shift();
|
|
213607
|
+
if (!current) {
|
|
213608
|
+
continue;
|
|
213609
|
+
}
|
|
213610
|
+
let entries;
|
|
213611
|
+
try {
|
|
213612
|
+
entries = await import_node_fs.promises.readdir(current.absolutePath, { withFileTypes: true });
|
|
213613
|
+
} catch {
|
|
213614
|
+
continue;
|
|
213615
|
+
}
|
|
213616
|
+
for (const entry of entries) {
|
|
213617
|
+
const entryPath = path.join(current.absolutePath, entry.name);
|
|
213618
|
+
if (entry.isDirectory()) {
|
|
213619
|
+
if (current.depth >= 2 || isIgnoredDirectoryName(entry.name)) {
|
|
213620
|
+
continue;
|
|
213621
|
+
}
|
|
213622
|
+
queue.push({ absolutePath: entryPath, depth: current.depth + 1 });
|
|
213623
|
+
continue;
|
|
213624
|
+
}
|
|
213625
|
+
if (!entry.isFile()) {
|
|
213626
|
+
continue;
|
|
213627
|
+
}
|
|
213628
|
+
if (sourceFileExtensions.has(path.extname(entry.name).toLowerCase())) {
|
|
213629
|
+
sourceFileCount += 1;
|
|
213630
|
+
}
|
|
213631
|
+
if (current.depth === 0 && publicEntrypointNames.has(entry.name.toLowerCase())) {
|
|
213632
|
+
hasPublicEntrypoint = true;
|
|
213633
|
+
}
|
|
213634
|
+
}
|
|
213635
|
+
}
|
|
213636
|
+
return { sourceFileCount, hasPublicEntrypoint };
|
|
213637
|
+
}
|
|
213638
|
+
async function addModuleIfStrongCandidate(workspaceRoot, discovered, sourcePath, moduleId, options) {
|
|
213639
|
+
const normalizedSourcePath = normalizePath(sourcePath);
|
|
213640
|
+
const name = normalizedSourcePath.split("/").pop() ?? moduleId;
|
|
213641
|
+
if (options?.allowNoisyNames !== true && isNoisyModuleName(name)) {
|
|
213642
|
+
return;
|
|
213643
|
+
}
|
|
213644
|
+
const absolutePath = path.join(workspaceRoot, ...normalizedSourcePath.split("/"));
|
|
213645
|
+
const inspected = await inspectPotentialModuleRoot(absolutePath);
|
|
213646
|
+
const hasStrongEvidence = inspected.sourceFileCount >= 2 || inspected.hasPublicEntrypoint || isKnownNestedModuleRoot(normalizedSourcePath);
|
|
213647
|
+
if (options?.requireStrongEvidence !== false && !hasStrongEvidence) {
|
|
213648
|
+
return;
|
|
213649
|
+
}
|
|
213650
|
+
const normalizedModuleId = normalizePath(moduleId || toModuleIdFromPath(normalizedSourcePath, options?.fallbackRoot));
|
|
213651
|
+
if (!normalizedModuleId || options?.allowNoisyNames !== true && isNoisyModuleName(normalizedModuleId.split("/").pop() ?? normalizedModuleId)) {
|
|
213652
|
+
return;
|
|
213653
|
+
}
|
|
213654
|
+
if (!discovered.has(normalizedModuleId)) {
|
|
213655
|
+
discovered.set(normalizedModuleId, {
|
|
213656
|
+
moduleId: normalizedModuleId,
|
|
213657
|
+
sourcePath: normalizedSourcePath
|
|
213658
|
+
});
|
|
213659
|
+
}
|
|
213660
|
+
}
|
|
213661
|
+
async function addChildrenFromRoot(workspaceRoot, discovered, rootRelativePath, options) {
|
|
213662
|
+
const normalizedRoot = normalizePath(rootRelativePath);
|
|
213663
|
+
const absoluteRoot = path.join(workspaceRoot, ...normalizedRoot.split("/"));
|
|
213664
|
+
if (!await pathExists(absoluteRoot)) {
|
|
213665
|
+
return;
|
|
213666
|
+
}
|
|
213667
|
+
const entries = (await import_node_fs.promises.readdir(absoluteRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && !isIgnoredDirectoryName(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
|
|
213668
|
+
for (const entry of entries) {
|
|
213669
|
+
const sourcePath = `${normalizedRoot}/${entry.name}`;
|
|
213670
|
+
const moduleId = options?.includeParentPrefix && options.parentId ? `${options.parentId}/${entry.name}` : toModuleIdFromPath(sourcePath, normalizedRoot);
|
|
213671
|
+
await addModuleIfStrongCandidate(workspaceRoot, discovered, sourcePath, moduleId, {
|
|
213672
|
+
fallbackRoot: normalizedRoot,
|
|
213673
|
+
...options?.requireStrongEvidence !== void 0 ? { requireStrongEvidence: options.requireStrongEvidence } : {},
|
|
213674
|
+
...options?.allowNoisyNames !== void 0 ? { allowNoisyNames: options.allowNoisyNames } : {}
|
|
213675
|
+
});
|
|
213676
|
+
}
|
|
213677
|
+
}
|
|
213678
|
+
async function addMonorepoContainerModules(workspaceRoot, discovered, container) {
|
|
213679
|
+
const absoluteContainer = path.join(workspaceRoot, container);
|
|
213680
|
+
if (!await pathExists(absoluteContainer)) {
|
|
213681
|
+
return;
|
|
213682
|
+
}
|
|
213683
|
+
const entries = (await import_node_fs.promises.readdir(absoluteContainer, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && !isIgnoredDirectoryName(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
|
|
213684
|
+
for (const entry of entries) {
|
|
213685
|
+
const packagePath = `${container}/${entry.name}`;
|
|
213686
|
+
await addModuleIfStrongCandidate(workspaceRoot, discovered, packagePath, entry.name, {
|
|
213687
|
+
requireStrongEvidence: false
|
|
213688
|
+
});
|
|
213689
|
+
for (const sourceRoot of [
|
|
213690
|
+
`${packagePath}/src`,
|
|
213691
|
+
`${packagePath}/src/modules`,
|
|
213692
|
+
`${packagePath}/src/features`,
|
|
213693
|
+
`${packagePath}/src/domain`,
|
|
213694
|
+
`${packagePath}/src/domains`,
|
|
213695
|
+
`${packagePath}/src/application`,
|
|
213696
|
+
`${packagePath}/src/infrastructure`
|
|
213697
|
+
]) {
|
|
213698
|
+
await addChildrenFromRoot(workspaceRoot, discovered, sourceRoot, {
|
|
213699
|
+
parentId: entry.name,
|
|
213700
|
+
includeParentPrefix: true
|
|
213701
|
+
});
|
|
213702
|
+
}
|
|
213703
|
+
}
|
|
213704
|
+
}
|
|
213705
|
+
async function discoverHierarchicalModuleRoots(workspaceRoot, modulesRootRelativePath = "src/modules") {
|
|
213706
|
+
const discovered = /* @__PURE__ */ new Map();
|
|
213707
|
+
await addChildrenFromRoot(workspaceRoot, discovered, normalizePath(modulesRootRelativePath), {
|
|
213708
|
+
requireStrongEvidence: false,
|
|
213709
|
+
allowNoisyNames: normalizePath(modulesRootRelativePath) === "src"
|
|
213710
|
+
});
|
|
213711
|
+
for (const root of [
|
|
213712
|
+
"src/modules",
|
|
213713
|
+
"src/features",
|
|
213714
|
+
"src/domain",
|
|
213715
|
+
"src/domains",
|
|
213716
|
+
"src/application",
|
|
213717
|
+
"src/infrastructure"
|
|
213718
|
+
]) {
|
|
213719
|
+
await addChildrenFromRoot(workspaceRoot, discovered, root);
|
|
213720
|
+
}
|
|
213721
|
+
for (const container of ["packages", "apps", "services"]) {
|
|
213722
|
+
await addMonorepoContainerModules(workspaceRoot, discovered, container);
|
|
213723
|
+
}
|
|
213724
|
+
return [...discovered.values()].sort((left, right) => left.moduleId.localeCompare(right.moduleId));
|
|
213725
|
+
}
|
|
213478
213726
|
async function discoverModuleRoots(workspaceRoot, modulesRootRelativePath = "src/modules") {
|
|
213727
|
+
const hierarchicalModules = await discoverHierarchicalModuleRoots(workspaceRoot, modulesRootRelativePath);
|
|
213728
|
+
if (hierarchicalModules.length > 0) {
|
|
213729
|
+
return new Map(
|
|
213730
|
+
hierarchicalModules.map((entry) => [
|
|
213731
|
+
entry.moduleId,
|
|
213732
|
+
path.join(workspaceRoot, ...entry.sourcePath.split("/"))
|
|
213733
|
+
])
|
|
213734
|
+
);
|
|
213735
|
+
}
|
|
213479
213736
|
const moduleDirectories = await listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath);
|
|
213480
213737
|
const modulesRoot = path.join(workspaceRoot, ...modulesRootRelativePath.split("/"));
|
|
213481
213738
|
return new Map(
|
|
@@ -214854,7 +215111,8 @@ function buildArchitectureContract(base, overrides) {
|
|
|
214854
215111
|
}
|
|
214855
215112
|
function buildDefaultModuleContractPath(moduleName, contract) {
|
|
214856
215113
|
const contractsRoot = resolveContractsRootFromContract(contract);
|
|
214857
|
-
|
|
215114
|
+
const safeModuleName = moduleName.replace(/\\/g, "/").split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
215115
|
+
return `${contractsRoot}/${safeModuleName}.contract.json`;
|
|
214858
215116
|
}
|
|
214859
215117
|
async function loadArchitectureContract(workspaceRoot) {
|
|
214860
215118
|
const configPath = path7.join(workspaceRoot, ".archpilot", "architecture.json");
|
|
@@ -214985,8 +215243,63 @@ var DependencyRulesConfigError = class extends Error {
|
|
|
214985
215243
|
function normalizePath2(filePath) {
|
|
214986
215244
|
return filePath.replace(/\\/g, "/");
|
|
214987
215245
|
}
|
|
214988
|
-
function
|
|
214989
|
-
return
|
|
215246
|
+
function toSafeContractModuleFileStem(moduleName) {
|
|
215247
|
+
return normalizePath2(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
215248
|
+
}
|
|
215249
|
+
function normalizeAbsolutePath(filePath) {
|
|
215250
|
+
return normalizePath2(path8.resolve(filePath)).toLowerCase();
|
|
215251
|
+
}
|
|
215252
|
+
function isPathInsideOrEqual(childPath, parentPath) {
|
|
215253
|
+
const child = normalizeAbsolutePath(childPath);
|
|
215254
|
+
const parent = normalizeAbsolutePath(parentPath);
|
|
215255
|
+
return child === parent || child.startsWith(`${parent}/`);
|
|
215256
|
+
}
|
|
215257
|
+
function isParentChildModuleRelationship(leftModule, rightModule) {
|
|
215258
|
+
if (leftModule === rightModule) {
|
|
215259
|
+
return false;
|
|
215260
|
+
}
|
|
215261
|
+
return leftModule.startsWith(`${rightModule}/`) || rightModule.startsWith(`${leftModule}/`);
|
|
215262
|
+
}
|
|
215263
|
+
function isPeerModuleImport(dependencyImport) {
|
|
215264
|
+
return !isParentChildModuleRelationship(
|
|
215265
|
+
dependencyImport.sourceModule,
|
|
215266
|
+
dependencyImport.targetModule
|
|
215267
|
+
);
|
|
215268
|
+
}
|
|
215269
|
+
function filterPeerModuleImports(imports) {
|
|
215270
|
+
return imports.filter(isPeerModuleImport);
|
|
215271
|
+
}
|
|
215272
|
+
function buildModuleHierarchyEdges(moduleIds) {
|
|
215273
|
+
const moduleIdSet = new Set(moduleIds);
|
|
215274
|
+
const edges = [];
|
|
215275
|
+
for (const moduleId of moduleIds) {
|
|
215276
|
+
const parentModule = moduleId.split("/").slice(0, -1).map((_, index, segments) => segments.slice(0, segments.length - index).join("/")).find((candidate) => moduleIdSet.has(candidate));
|
|
215277
|
+
if (!parentModule) {
|
|
215278
|
+
continue;
|
|
215279
|
+
}
|
|
215280
|
+
edges.push({ parentModule, childModule: moduleId });
|
|
215281
|
+
}
|
|
215282
|
+
return edges.sort((left, right) => {
|
|
215283
|
+
const parentCompare = left.parentModule.localeCompare(right.parentModule);
|
|
215284
|
+
if (parentCompare !== 0) {
|
|
215285
|
+
return parentCompare;
|
|
215286
|
+
}
|
|
215287
|
+
return left.childModule.localeCompare(right.childModule);
|
|
215288
|
+
});
|
|
215289
|
+
}
|
|
215290
|
+
function buildChildModulesByParent(hierarchyEdges) {
|
|
215291
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
215292
|
+
for (const edge of hierarchyEdges) {
|
|
215293
|
+
const children = byParent.get(edge.parentModule) ?? /* @__PURE__ */ new Set();
|
|
215294
|
+
children.add(edge.childModule);
|
|
215295
|
+
byParent.set(edge.parentModule, children);
|
|
215296
|
+
}
|
|
215297
|
+
return new Map(
|
|
215298
|
+
[...byParent.entries()].map(([parentModule, childModules]) => [
|
|
215299
|
+
parentModule,
|
|
215300
|
+
sortUnique2(childModules)
|
|
215301
|
+
])
|
|
215302
|
+
);
|
|
214990
215303
|
}
|
|
214991
215304
|
function getScriptKind(filePath) {
|
|
214992
215305
|
const extension = path8.extname(filePath).toLowerCase();
|
|
@@ -215027,6 +215340,37 @@ async function readTextFileIfExists2(filePath) {
|
|
|
215027
215340
|
return void 0;
|
|
215028
215341
|
}
|
|
215029
215342
|
}
|
|
215343
|
+
async function readPackageName(packageJsonPath) {
|
|
215344
|
+
const contents = await readTextFileIfExists2(packageJsonPath);
|
|
215345
|
+
if (!contents) {
|
|
215346
|
+
return void 0;
|
|
215347
|
+
}
|
|
215348
|
+
try {
|
|
215349
|
+
const parsed = JSON.parse(contents);
|
|
215350
|
+
return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : void 0;
|
|
215351
|
+
} catch {
|
|
215352
|
+
return void 0;
|
|
215353
|
+
}
|
|
215354
|
+
}
|
|
215355
|
+
async function buildWorkspacePackageAliasMap(moduleRoots) {
|
|
215356
|
+
const aliases = [];
|
|
215357
|
+
for (const [moduleId, moduleRoot] of [...moduleRoots.entries()].sort(
|
|
215358
|
+
([left], [right]) => left.localeCompare(right)
|
|
215359
|
+
)) {
|
|
215360
|
+
const packageName = await readPackageName(path8.join(moduleRoot, "package.json"));
|
|
215361
|
+
if (!packageName) {
|
|
215362
|
+
continue;
|
|
215363
|
+
}
|
|
215364
|
+
aliases.push({ packageName, moduleId, moduleRoot });
|
|
215365
|
+
}
|
|
215366
|
+
return aliases.sort((left, right) => {
|
|
215367
|
+
const lengthCompare = right.packageName.length - left.packageName.length;
|
|
215368
|
+
if (lengthCompare !== 0) {
|
|
215369
|
+
return lengthCompare;
|
|
215370
|
+
}
|
|
215371
|
+
return left.packageName.localeCompare(right.packageName);
|
|
215372
|
+
});
|
|
215373
|
+
}
|
|
215030
215374
|
function validateDependencyModuleRules(moduleName, value) {
|
|
215031
215375
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
215032
215376
|
throw new DependencyRulesConfigError(
|
|
@@ -215500,7 +215844,32 @@ async function resolveImportPath(basePath, adapter) {
|
|
|
215500
215844
|
}
|
|
215501
215845
|
return void 0;
|
|
215502
215846
|
}
|
|
215503
|
-
|
|
215847
|
+
function resolvePackageAliasRemainder(packageName, importSpecifier) {
|
|
215848
|
+
if (importSpecifier === packageName) {
|
|
215849
|
+
return "";
|
|
215850
|
+
}
|
|
215851
|
+
if (importSpecifier.startsWith(`${packageName}/`)) {
|
|
215852
|
+
return importSpecifier.slice(packageName.length + 1);
|
|
215853
|
+
}
|
|
215854
|
+
return void 0;
|
|
215855
|
+
}
|
|
215856
|
+
async function resolveWorkspacePackageImportPath(importSpecifier, workspacePackageAliases, adapter) {
|
|
215857
|
+
const packageAlias = workspacePackageAliases.map((alias2) => ({
|
|
215858
|
+
alias: alias2,
|
|
215859
|
+
remainder: resolvePackageAliasRemainder(alias2.packageName, importSpecifier)
|
|
215860
|
+
})).find(
|
|
215861
|
+
(candidate) => candidate.remainder !== void 0
|
|
215862
|
+
);
|
|
215863
|
+
if (!packageAlias) {
|
|
215864
|
+
return void 0;
|
|
215865
|
+
}
|
|
215866
|
+
const { alias, remainder } = packageAlias;
|
|
215867
|
+
if (remainder.length === 0) {
|
|
215868
|
+
return resolveImportPath(alias.moduleRoot, adapter);
|
|
215869
|
+
}
|
|
215870
|
+
return void 0;
|
|
215871
|
+
}
|
|
215872
|
+
async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, workspacePackageAliases, sourceFilePath, importSpecifier, adapter) {
|
|
215504
215873
|
let basePath;
|
|
215505
215874
|
const normalizedModulesRoot = normalizePath2(modulesRootRelativePath).replace(/^\/+/, "");
|
|
215506
215875
|
if (usesJavaImportParser(adapter) && !importSpecifier.startsWith(".")) {
|
|
@@ -215555,6 +215924,22 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215555
215924
|
basePath = path8.join(workspaceRoot, importSpecifier.slice(1));
|
|
215556
215925
|
} else if (normalizedModulesRoot.startsWith("src/") && importSpecifier.startsWith(`${normalizedModulesRoot.slice("src/".length)}/`)) {
|
|
215557
215926
|
basePath = path8.join(workspaceRoot, "src", importSpecifier);
|
|
215927
|
+
} else {
|
|
215928
|
+
const workspacePackageImportPath = await resolveWorkspacePackageImportPath(
|
|
215929
|
+
importSpecifier,
|
|
215930
|
+
workspacePackageAliases,
|
|
215931
|
+
adapter
|
|
215932
|
+
);
|
|
215933
|
+
if (workspacePackageImportPath) {
|
|
215934
|
+
basePath = workspacePackageImportPath;
|
|
215935
|
+
} else {
|
|
215936
|
+
const moduleImportTarget = [...moduleRoots.entries()].filter(([moduleId]) => importSpecifier === moduleId || importSpecifier.startsWith(`${moduleId}/`)).sort((left, right) => right[0].length - left[0].length || left[0].localeCompare(right[0]))[0];
|
|
215937
|
+
if (moduleImportTarget) {
|
|
215938
|
+
const [moduleId, moduleRoot] = moduleImportTarget;
|
|
215939
|
+
const remainder = importSpecifier === moduleId ? "" : importSpecifier.slice(moduleId.length + 1);
|
|
215940
|
+
basePath = path8.join(moduleRoot, ...remainder.split("/").filter((segment) => segment.length > 0));
|
|
215941
|
+
}
|
|
215942
|
+
}
|
|
215558
215943
|
}
|
|
215559
215944
|
if (!basePath) {
|
|
215560
215945
|
return void 0;
|
|
@@ -215563,17 +215948,8 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215563
215948
|
if (!resolvedPath) {
|
|
215564
215949
|
return void 0;
|
|
215565
215950
|
}
|
|
215566
|
-
const
|
|
215567
|
-
|
|
215568
|
-
if (relativeToModulesRoot === "" || relativeToModulesRoot.startsWith("../") || relativeToModulesRoot.startsWith("..\\")) {
|
|
215569
|
-
return void 0;
|
|
215570
|
-
}
|
|
215571
|
-
const segments = relativeToModulesRoot.split("/");
|
|
215572
|
-
if (segments.length === 0) {
|
|
215573
|
-
return void 0;
|
|
215574
|
-
}
|
|
215575
|
-
const targetModule = segments[0];
|
|
215576
|
-
if (!moduleRoots.has(targetModule)) {
|
|
215951
|
+
const targetModuleEntry = [...moduleRoots.entries()].filter(([, moduleRoot]) => isPathInsideOrEqual(resolvedPath, moduleRoot)).sort((left, right) => right[1].length - left[1].length || left[0].localeCompare(right[0]))[0];
|
|
215952
|
+
if (!targetModuleEntry) {
|
|
215577
215953
|
const standaloneTargetModule = explicitStandaloneModuleByPath.get(
|
|
215578
215954
|
normalizePath2(path8.relative(workspaceRoot, resolvedPath))
|
|
215579
215955
|
);
|
|
@@ -215586,7 +215962,11 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215586
215962
|
isPublicImport: true
|
|
215587
215963
|
};
|
|
215588
215964
|
}
|
|
215589
|
-
const
|
|
215965
|
+
const [targetModule, targetModuleRoot] = targetModuleEntry;
|
|
215966
|
+
const targetSubPath = normalizePath2(path8.relative(targetModuleRoot, resolvedPath));
|
|
215967
|
+
if (targetSubPath === "" || targetSubPath.startsWith("../") || targetSubPath.startsWith("..\\")) {
|
|
215968
|
+
return void 0;
|
|
215969
|
+
}
|
|
215590
215970
|
const indexEntrypointCandidates = adapter.dependencyParsingFileExtensions.map(
|
|
215591
215971
|
(extension) => `index${extension}`
|
|
215592
215972
|
);
|
|
@@ -215599,7 +215979,7 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215599
215979
|
isPublicImport
|
|
215600
215980
|
};
|
|
215601
215981
|
}
|
|
215602
|
-
async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, adapter, options) {
|
|
215982
|
+
async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, workspacePackageAliases, adapter, options) {
|
|
215603
215983
|
const imports = [];
|
|
215604
215984
|
const includeTestFiles = options?.includeTestFiles === true;
|
|
215605
215985
|
if (!supportsDependencyImportParsing(adapter)) {
|
|
@@ -215608,7 +215988,10 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
|
|
|
215608
215988
|
for (const [moduleName, moduleRoot] of [...moduleRoots.entries()].sort(
|
|
215609
215989
|
([left], [right]) => left.localeCompare(right)
|
|
215610
215990
|
)) {
|
|
215611
|
-
const
|
|
215991
|
+
const childModuleRoots = [...moduleRoots.entries()].filter(([childModuleName, childModuleRoot]) => childModuleName !== moduleName && isPathInsideOrEqual(childModuleRoot, moduleRoot)).map(([, childModuleRoot]) => childModuleRoot);
|
|
215992
|
+
const sourceFiles = (await collectSourceFiles(moduleRoot, adapter)).filter(
|
|
215993
|
+
(sourceFile) => !childModuleRoots.some((childModuleRoot) => isPathInsideOrEqual(sourceFile, childModuleRoot))
|
|
215994
|
+
);
|
|
215612
215995
|
for (const sourceFilePath of sourceFiles) {
|
|
215613
215996
|
const sourceFileRelativePath = normalizePath2(
|
|
215614
215997
|
path8.relative(workspaceRoot, sourceFilePath)
|
|
@@ -215631,6 +216014,7 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
|
|
|
215631
216014
|
modulesRootRelativePath,
|
|
215632
216015
|
moduleRoots,
|
|
215633
216016
|
explicitStandaloneModuleByPath,
|
|
216017
|
+
workspacePackageAliases,
|
|
215634
216018
|
sourceFilePath,
|
|
215635
216019
|
importReference.specifier,
|
|
215636
216020
|
adapter
|
|
@@ -215776,7 +216160,7 @@ async function loadModuleArchitectureContract(workspaceRoot, architectureContrac
|
|
|
215776
216160
|
const registryEntry = architectureContract.modules[moduleName];
|
|
215777
216161
|
const contractPath = registryEntry?.contract ?? path8.posix.join(
|
|
215778
216162
|
resolveContractsRootFromContract(architectureContract),
|
|
215779
|
-
`${moduleName}.contract.json`
|
|
216163
|
+
`${toSafeContractModuleFileStem(moduleName)}.contract.json`
|
|
215780
216164
|
);
|
|
215781
216165
|
const fullContractPath = path8.join(workspaceRoot, ...contractPath.split("/"));
|
|
215782
216166
|
const content = await readTextFileIfExists2(fullContractPath);
|
|
@@ -215923,6 +216307,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215923
216307
|
workspaceRoot,
|
|
215924
216308
|
architectureContract
|
|
215925
216309
|
);
|
|
216310
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
215926
216311
|
const moduleIds = sortUnique2([...moduleRoots.keys(), ...explicitStandaloneModuleByPath.values()]);
|
|
215927
216312
|
const registeredModuleIds = new Set(moduleIds);
|
|
215928
216313
|
const imports = await collectCrossModuleImports(
|
|
@@ -215930,9 +216315,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215930
216315
|
modulesRoot,
|
|
215931
216316
|
moduleRoots,
|
|
215932
216317
|
explicitStandaloneModuleByPath,
|
|
216318
|
+
workspacePackageAliases,
|
|
215933
216319
|
adapter
|
|
215934
216320
|
);
|
|
215935
|
-
const
|
|
216321
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
216322
|
+
const actualBySource = buildActualImportedModuleSetBySource(peerImports, registeredModuleIds);
|
|
215936
216323
|
const moduleContractCache = /* @__PURE__ */ new Map();
|
|
215937
216324
|
const declaredBySource = /* @__PURE__ */ new Map();
|
|
215938
216325
|
const hasContractByModule = /* @__PURE__ */ new Map();
|
|
@@ -215950,7 +216337,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215950
216337
|
new Set(
|
|
215951
216338
|
sortUnique2(
|
|
215952
216339
|
sourceContract.contract.dependsOn.filter(
|
|
215953
|
-
(dependencyModule) => registeredModuleIds.has(dependencyModule)
|
|
216340
|
+
(dependencyModule) => registeredModuleIds.has(dependencyModule) && !isParentChildModuleRelationship(moduleId, dependencyModule)
|
|
215954
216341
|
)
|
|
215955
216342
|
)
|
|
215956
216343
|
)
|
|
@@ -215962,6 +216349,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215962
216349
|
}
|
|
215963
216350
|
const inboundDeclared = buildInboundDependencyMap(declaredBySource, moduleIds);
|
|
215964
216351
|
const inboundActual = buildInboundDependencyMap(actualBySource, moduleIds);
|
|
216352
|
+
const hierarchyEdges = buildModuleHierarchyEdges(moduleIds);
|
|
216353
|
+
const childModulesByParent = buildChildModulesByParent(hierarchyEdges);
|
|
216354
|
+
const parentModuleByChild = new Map(
|
|
216355
|
+
hierarchyEdges.map((edge) => [edge.childModule, edge.parentModule])
|
|
216356
|
+
);
|
|
215965
216357
|
return {
|
|
215966
216358
|
modules: moduleIds.map((moduleId) => {
|
|
215967
216359
|
const declaredDependencies = sortUnique2(declaredBySource.get(moduleId) ?? []);
|
|
@@ -215975,6 +216367,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215975
216367
|
return {
|
|
215976
216368
|
module: moduleId,
|
|
215977
216369
|
hasContract: hasContractByModule.get(moduleId) ?? false,
|
|
216370
|
+
...parentModuleByChild.has(moduleId) ? { parentModule: parentModuleByChild.get(moduleId) } : {},
|
|
216371
|
+
childModules: childModulesByParent.get(moduleId) ?? [],
|
|
215978
216372
|
declaredDependencies,
|
|
215979
216373
|
actualDependencies,
|
|
215980
216374
|
inboundDeclaredFrom: inboundDeclared.get(moduleId) ?? [],
|
|
@@ -215982,7 +216376,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215982
216376
|
unusedDeclaredDependencies,
|
|
215983
216377
|
undeclaredActualDependencies
|
|
215984
216378
|
};
|
|
215985
|
-
})
|
|
216379
|
+
}),
|
|
216380
|
+
hierarchyEdges
|
|
215986
216381
|
};
|
|
215987
216382
|
}
|
|
215988
216383
|
async function validateDependencyContractBoundaries(workspaceRoot, architectureContract, options) {
|
|
@@ -215996,19 +216391,22 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215996
216391
|
workspaceRoot,
|
|
215997
216392
|
architectureContract
|
|
215998
216393
|
);
|
|
216394
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
215999
216395
|
const imports = await collectCrossModuleImports(
|
|
216000
216396
|
workspaceRoot,
|
|
216001
216397
|
modulesRoot,
|
|
216002
216398
|
moduleRoots,
|
|
216003
216399
|
explicitStandaloneModuleByPath,
|
|
216400
|
+
workspacePackageAliases,
|
|
216004
216401
|
adapter
|
|
216005
216402
|
);
|
|
216403
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
216006
216404
|
const findings = [];
|
|
216007
216405
|
const moduleContractCache = /* @__PURE__ */ new Map();
|
|
216008
216406
|
if (options.checkDeclaredDependencies) {
|
|
216009
216407
|
const failures = [];
|
|
216010
216408
|
const skippedFindings = /* @__PURE__ */ new Map();
|
|
216011
|
-
for (const dependencyImport of
|
|
216409
|
+
for (const dependencyImport of peerImports) {
|
|
216012
216410
|
const sourceContract = await loadModuleArchitectureContract(
|
|
216013
216411
|
workspaceRoot,
|
|
216014
216412
|
architectureContract,
|
|
@@ -216080,7 +216478,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216080
216478
|
}
|
|
216081
216479
|
if (failures.length > 0) {
|
|
216082
216480
|
findings.push(...failures);
|
|
216083
|
-
} else if (
|
|
216481
|
+
} else if (peerImports.length === 0) {
|
|
216084
216482
|
findings.push({
|
|
216085
216483
|
result: makeValidationResult(
|
|
216086
216484
|
"AP-DEP-004",
|
|
@@ -216105,7 +216503,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216105
216503
|
if (options.checkPublicSurfaceImports) {
|
|
216106
216504
|
const failures = [];
|
|
216107
216505
|
const skippedFindings = /* @__PURE__ */ new Map();
|
|
216108
|
-
for (const dependencyImport of
|
|
216506
|
+
for (const dependencyImport of peerImports) {
|
|
216109
216507
|
const sourceContract = await loadModuleArchitectureContract(
|
|
216110
216508
|
workspaceRoot,
|
|
216111
216509
|
architectureContract,
|
|
@@ -216172,17 +216570,18 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216172
216570
|
}
|
|
216173
216571
|
continue;
|
|
216174
216572
|
}
|
|
216175
|
-
if (targetContract.contract.publicEntrypoints.length > 0 &&
|
|
216176
|
-
|
|
216177
|
-
|
|
216178
|
-
adapter
|
|
216573
|
+
if (targetContract.contract.publicEntrypoints.length > 0 && !isImportWithinPublicEntrypoints(
|
|
216574
|
+
dependencyImport.resolvedTargetRelativePath,
|
|
216575
|
+
targetContract.contract.publicEntrypoints
|
|
216179
216576
|
)) {
|
|
216577
|
+
const expectedEntrypoint = targetContract.contract.publicEntrypoints[0] ?? buildModuleIndexEntrypointPath(modulesRoot, dependencyImport.targetModule, adapter);
|
|
216578
|
+
const publicEntrypointLabel = expectedEntrypoint.endsWith("/index.ts") ? "index.ts public entrypoint" : "public entrypoint";
|
|
216180
216579
|
failures.push({
|
|
216181
216580
|
result: makeValidationResult(
|
|
216182
216581
|
"AP-DEP-005",
|
|
216183
216582
|
"error",
|
|
216184
216583
|
false,
|
|
216185
|
-
`Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its
|
|
216584
|
+
`Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its ${publicEntrypointLabel}: ${expectedEntrypoint}.`,
|
|
216186
216585
|
{
|
|
216187
216586
|
findingType: "module-dependency",
|
|
216188
216587
|
module: dependencyImport.sourceModule,
|
|
@@ -216223,7 +216622,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216223
216622
|
}
|
|
216224
216623
|
if (failures.length > 0) {
|
|
216225
216624
|
findings.push(...failures);
|
|
216226
|
-
} else if (
|
|
216625
|
+
} else if (peerImports.length === 0) {
|
|
216227
216626
|
findings.push({
|
|
216228
216627
|
result: makeValidationResult(
|
|
216229
216628
|
"AP-DEP-005",
|
|
@@ -216252,7 +216651,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216252
216651
|
...explicitStandaloneModuleByPath.values()
|
|
216253
216652
|
]);
|
|
216254
216653
|
const importedBySource = buildActualImportedModuleSetBySource(
|
|
216255
|
-
|
|
216654
|
+
peerImports,
|
|
216256
216655
|
registeredModuleIds
|
|
216257
216656
|
);
|
|
216258
216657
|
for (const moduleName of [...registeredModuleIds].sort(
|
|
@@ -216267,7 +216666,9 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
216267
216666
|
if (sourceContract.status !== "ok" || !sourceContract.contract) {
|
|
216268
216667
|
continue;
|
|
216269
216668
|
}
|
|
216270
|
-
const declaredDependencies = [...sourceContract.contract.dependsOn].filter(
|
|
216669
|
+
const declaredDependencies = [...sourceContract.contract.dependsOn].filter(
|
|
216670
|
+
(dependencyModule) => registeredModuleIds.has(dependencyModule) && !isParentChildModuleRelationship(moduleName, dependencyModule)
|
|
216671
|
+
).sort((left, right) => left.localeCompare(right));
|
|
216271
216672
|
if (declaredDependencies.length === 0) {
|
|
216272
216673
|
continue;
|
|
216273
216674
|
}
|
|
@@ -216322,13 +216723,16 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216322
216723
|
workspaceRoot,
|
|
216323
216724
|
parsedArchitectureContract
|
|
216324
216725
|
);
|
|
216726
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
216325
216727
|
const imports = await collectCrossModuleImports(
|
|
216326
216728
|
workspaceRoot,
|
|
216327
216729
|
modulesRootRelativePath,
|
|
216328
216730
|
moduleRoots,
|
|
216329
216731
|
explicitStandaloneModuleByPath,
|
|
216732
|
+
workspacePackageAliases,
|
|
216330
216733
|
adapter
|
|
216331
216734
|
);
|
|
216735
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
216332
216736
|
const findings = [];
|
|
216333
216737
|
let dependencyRules;
|
|
216334
216738
|
let dependencyRulesSkipReason;
|
|
@@ -216349,7 +216753,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216349
216753
|
}
|
|
216350
216754
|
}
|
|
216351
216755
|
if (options.checkCircularDependencies) {
|
|
216352
|
-
const cycles = detectCircularDependencies([...moduleRoots.keys()],
|
|
216756
|
+
const cycles = detectCircularDependencies([...moduleRoots.keys()], peerImports);
|
|
216353
216757
|
if (cycles.length === 0) {
|
|
216354
216758
|
findings.push({
|
|
216355
216759
|
result: makeValidationResult(
|
|
@@ -216381,7 +216785,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216381
216785
|
})
|
|
216382
216786
|
);
|
|
216383
216787
|
} else {
|
|
216384
|
-
const violations =
|
|
216788
|
+
const violations = peerImports.filter((dependencyImport) => {
|
|
216385
216789
|
const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
|
|
216386
216790
|
if (!moduleRules) {
|
|
216387
216791
|
return false;
|
|
@@ -216433,7 +216837,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216433
216837
|
})
|
|
216434
216838
|
);
|
|
216435
216839
|
} else {
|
|
216436
|
-
const violations =
|
|
216840
|
+
const violations = peerImports.filter((dependencyImport) => {
|
|
216437
216841
|
const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
|
|
216438
216842
|
return moduleRules?.publicEntrypointsOnly === true && !dependencyImport.isPublicImport;
|
|
216439
216843
|
});
|
|
@@ -216481,6 +216885,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216481
216885
|
workspaceRoot,
|
|
216482
216886
|
parsedArchitectureContract
|
|
216483
216887
|
);
|
|
216888
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
216484
216889
|
const discoveredModules = sortUnique2([
|
|
216485
216890
|
...moduleRoots.keys(),
|
|
216486
216891
|
...explicitStandaloneModuleByPath.values()
|
|
@@ -216493,9 +216898,10 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216493
216898
|
modulesRootRelativePath,
|
|
216494
216899
|
moduleRoots,
|
|
216495
216900
|
explicitStandaloneModuleByPath,
|
|
216901
|
+
workspacePackageAliases,
|
|
216496
216902
|
adapter
|
|
216497
216903
|
);
|
|
216498
|
-
const imports = allImports.filter(
|
|
216904
|
+
const imports = filterPeerModuleImports(allImports).filter(
|
|
216499
216905
|
(dependencyImport) => moduleSet.has(dependencyImport.sourceModule) && moduleSet.has(dependencyImport.targetModule)
|
|
216500
216906
|
).sort((left, right) => {
|
|
216501
216907
|
const sourceCompare = left.sourceModule.localeCompare(right.sourceModule);
|
|
@@ -216548,6 +216954,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216548
216954
|
return {
|
|
216549
216955
|
modules,
|
|
216550
216956
|
edges,
|
|
216957
|
+
hierarchyEdges: buildModuleHierarchyEdges(modules),
|
|
216551
216958
|
cycles,
|
|
216552
216959
|
imports
|
|
216553
216960
|
};
|
|
@@ -219949,11 +220356,11 @@ src/
|
|
|
219949
220356
|
},
|
|
219950
220357
|
DEP_ISOLATED_MODULE_DETECTED: {
|
|
219951
220358
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
219952
|
-
title: "
|
|
220359
|
+
title: "Potential orphan module detected",
|
|
219953
220360
|
category: "dependency",
|
|
219954
220361
|
defaultSeverity: "warning",
|
|
219955
|
-
description: "Checks whether a registered module has no
|
|
219956
|
-
recommendedFix: "
|
|
220362
|
+
description: "Checks whether a registered module has no peer-module dependencies and little evidence of intentional architectural use.",
|
|
220363
|
+
recommendedFix: "Add intentional-use evidence such as a contract, README, public entrypoint, package entrypoint, or remove the module if it is stale."
|
|
219957
220364
|
},
|
|
219958
220365
|
DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY: {
|
|
219959
220366
|
id: ValidationRuleIds.DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY,
|
|
@@ -221172,8 +221579,17 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
|
|
|
221172
221579
|
const modulesRootRelativePath2 = await getConfiguredModulesRoot(workspaceRoot, contract);
|
|
221173
221580
|
const modulesRoot = path21.join(workspaceRoot, ...modulesRootRelativePath2.split("/"));
|
|
221174
221581
|
if (await pathExists7(modulesRoot)) {
|
|
221175
|
-
const
|
|
221176
|
-
|
|
221582
|
+
const hierarchicalModules = await discoverHierarchicalModuleRoots(
|
|
221583
|
+
workspaceRoot,
|
|
221584
|
+
modulesRootRelativePath2
|
|
221585
|
+
);
|
|
221586
|
+
const moduleDirectories2 = hierarchicalModules.length > 0 ? hierarchicalModules.map((entry) => ({
|
|
221587
|
+
moduleName: entry.moduleId,
|
|
221588
|
+
sourcePath: entry.sourcePath
|
|
221589
|
+
})) : (await import_node_fs20.promises.readdir(modulesRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
221590
|
+
moduleName: entry.name,
|
|
221591
|
+
sourcePath: `${modulesRootRelativePath2}/${entry.name}`
|
|
221592
|
+
})).sort((left, right) => left.moduleName.localeCompare(right.moduleName));
|
|
221177
221593
|
const dependencySummary = await buildDependencyGraphSummary(workspaceRoot, contract, {
|
|
221178
221594
|
modulesRootRelativePath: modulesRootRelativePath2
|
|
221179
221595
|
});
|
|
@@ -221181,8 +221597,9 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
|
|
|
221181
221597
|
dependencySummary.modules.filter((moduleSummary) => moduleSummary.inboundDeclaredFrom.length > 0).map((moduleSummary) => moduleSummary.module)
|
|
221182
221598
|
);
|
|
221183
221599
|
const incompleteModules = [];
|
|
221184
|
-
for (const
|
|
221185
|
-
const
|
|
221600
|
+
for (const moduleDirectory of moduleDirectories2) {
|
|
221601
|
+
const moduleName = moduleDirectory.moduleName;
|
|
221602
|
+
const moduleDir = path21.join(workspaceRoot, ...moduleDirectory.sourcePath.split("/"));
|
|
221186
221603
|
const readmeExists = await pathExists7(path21.join(moduleDir, "README.md"));
|
|
221187
221604
|
const indexExists = await pathExists7(path21.join(moduleDir, "index.ts"));
|
|
221188
221605
|
const registryPublicEntrypoints = (contract.modules[moduleName]?.publicEntrypoints ?? []).filter((entry) => typeof entry === "string").map((entry) => normalizeEntrypoint(entry));
|
|
@@ -221311,18 +221728,111 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
221311
221728
|
return [];
|
|
221312
221729
|
}
|
|
221313
221730
|
const isolatedResults = [];
|
|
221314
|
-
const
|
|
221731
|
+
const sourceFileExtensions4 = /* @__PURE__ */ new Set([
|
|
221732
|
+
".ts",
|
|
221733
|
+
".tsx",
|
|
221734
|
+
".js",
|
|
221735
|
+
".jsx",
|
|
221736
|
+
".mjs",
|
|
221737
|
+
".cjs",
|
|
221738
|
+
".mts",
|
|
221739
|
+
".cts",
|
|
221740
|
+
".py",
|
|
221741
|
+
".go",
|
|
221742
|
+
".java",
|
|
221743
|
+
".kt",
|
|
221744
|
+
".kts",
|
|
221745
|
+
".cs",
|
|
221746
|
+
".php",
|
|
221747
|
+
".rb",
|
|
221748
|
+
".vue",
|
|
221749
|
+
".svelte"
|
|
221750
|
+
]);
|
|
221315
221751
|
const isTestLikeModule = (moduleName) => {
|
|
221316
221752
|
const normalized = moduleName.toLowerCase();
|
|
221317
221753
|
return normalized === "test" || normalized === "tests" || normalized.endsWith("-test") || normalized.endsWith("-tests");
|
|
221318
221754
|
};
|
|
221755
|
+
const collectModuleSourceEvidence2 = async (directoryPath) => {
|
|
221756
|
+
let entries;
|
|
221757
|
+
try {
|
|
221758
|
+
entries = await import_node_fs20.promises.readdir(directoryPath, { withFileTypes: true });
|
|
221759
|
+
} catch {
|
|
221760
|
+
return { sourceFileCount: 0, hasReadme: false, hasEntrypointFile: false };
|
|
221761
|
+
}
|
|
221762
|
+
let sourceFileCount = 0;
|
|
221763
|
+
let hasReadme = false;
|
|
221764
|
+
let hasEntrypointFile = false;
|
|
221765
|
+
for (const entry of entries) {
|
|
221766
|
+
const fullPath = path21.join(directoryPath, entry.name);
|
|
221767
|
+
if (entry.isDirectory()) {
|
|
221768
|
+
if (["node_modules", "dist", "out", "build", "coverage", ".git"].includes(entry.name)) {
|
|
221769
|
+
continue;
|
|
221770
|
+
}
|
|
221771
|
+
const nested = await collectModuleSourceEvidence2(fullPath);
|
|
221772
|
+
sourceFileCount += nested.sourceFileCount;
|
|
221773
|
+
hasReadme ||= nested.hasReadme;
|
|
221774
|
+
hasEntrypointFile ||= nested.hasEntrypointFile;
|
|
221775
|
+
continue;
|
|
221776
|
+
}
|
|
221777
|
+
if (!entry.isFile()) {
|
|
221778
|
+
continue;
|
|
221779
|
+
}
|
|
221780
|
+
const lowerName = entry.name.toLowerCase();
|
|
221781
|
+
if (lowerName === "readme.md" || lowerName === "readme.mdx") {
|
|
221782
|
+
hasReadme = true;
|
|
221783
|
+
}
|
|
221784
|
+
if (/^(index|main|app|extension|cli)\.[cm]?[jt]sx?$/iu.test(entry.name)) {
|
|
221785
|
+
hasEntrypointFile = true;
|
|
221786
|
+
}
|
|
221787
|
+
if (sourceFileExtensions4.has(path21.extname(entry.name).toLowerCase())) {
|
|
221788
|
+
sourceFileCount += 1;
|
|
221789
|
+
}
|
|
221790
|
+
}
|
|
221791
|
+
return { sourceFileCount, hasReadme, hasEntrypointFile };
|
|
221792
|
+
};
|
|
221793
|
+
const readPackageJsonIfExists2 = async (moduleDirectoryPath) => {
|
|
221794
|
+
const content = await readTextFileIfExists3(path21.join(moduleDirectoryPath, "package.json"));
|
|
221795
|
+
if (!content) {
|
|
221796
|
+
return void 0;
|
|
221797
|
+
}
|
|
221798
|
+
try {
|
|
221799
|
+
const parsed = JSON.parse(content);
|
|
221800
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
221801
|
+
} catch {
|
|
221802
|
+
return void 0;
|
|
221803
|
+
}
|
|
221804
|
+
};
|
|
221805
|
+
const hasPackageEntrypoint2 = async (moduleDirectoryPath) => {
|
|
221806
|
+
const packageJson = await readPackageJsonIfExists2(moduleDirectoryPath);
|
|
221807
|
+
if (!packageJson) {
|
|
221808
|
+
return false;
|
|
221809
|
+
}
|
|
221810
|
+
return ["main", "module", "browser", "types", "bin", "exports"].some(
|
|
221811
|
+
(field) => packageJson[field] !== void 0
|
|
221812
|
+
);
|
|
221813
|
+
};
|
|
221814
|
+
const hasKnownBoundaryRole2 = (moduleName) => {
|
|
221815
|
+
const normalized = moduleName.toLowerCase();
|
|
221816
|
+
const compact = normalized.replace(/[-_/]/gu, "");
|
|
221817
|
+
const segments = normalized.split(/[/-]/u);
|
|
221818
|
+
return ["adr", "ci", "compliance", "config", "governance", "policy", "validation", "smartinit"].some(
|
|
221819
|
+
(role) => compact.includes(role) || segments.includes(role)
|
|
221820
|
+
);
|
|
221821
|
+
};
|
|
221822
|
+
const hasConfiguredPublicEntrypoint2 = (moduleName) => {
|
|
221823
|
+
const publicEntrypoints = contract.modules[moduleName]?.publicEntrypoints;
|
|
221824
|
+
return Array.isArray(publicEntrypoints) && publicEntrypoints.some(
|
|
221825
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
221826
|
+
);
|
|
221827
|
+
};
|
|
221828
|
+
const hasIntentionalIndependentEvidence2 = async (moduleSummary, moduleDirectoryPath) => {
|
|
221829
|
+
const sourceEvidence = await collectModuleSourceEvidence2(moduleDirectoryPath);
|
|
221830
|
+
return moduleSummary.hasContract || hasConfiguredPublicEntrypoint2(moduleSummary.module) || sourceEvidence.hasReadme || sourceEvidence.hasEntrypointFile || await hasPackageEntrypoint2(moduleDirectoryPath) || moduleSummary.childModules.length > 0 || moduleSummary.parentModule !== void 0 && moduleSummary.childModules.length === 0 && sourceEvidence.sourceFileCount > 0 || hasKnownBoundaryRole2(moduleSummary.module) || sourceEvidence.sourceFileCount >= 2;
|
|
221831
|
+
};
|
|
221319
221832
|
for (const moduleSummary of dependencyGraphSummary.modules) {
|
|
221320
221833
|
if (isTestLikeModule(moduleSummary.module)) {
|
|
221321
221834
|
continue;
|
|
221322
221835
|
}
|
|
221323
|
-
if (isolationExemptInfrastructureModules.has(moduleSummary.module.toLowerCase())) {
|
|
221324
|
-
continue;
|
|
221325
|
-
}
|
|
221326
221836
|
const registryEntry = contract.modules[moduleSummary.module];
|
|
221327
221837
|
if (!registryEntry?.path) {
|
|
221328
221838
|
continue;
|
|
@@ -221336,11 +221846,14 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
221336
221846
|
if (!isIsolated) {
|
|
221337
221847
|
continue;
|
|
221338
221848
|
}
|
|
221849
|
+
if (await hasIntentionalIndependentEvidence2(moduleSummary, moduleDirectoryPath)) {
|
|
221850
|
+
continue;
|
|
221851
|
+
}
|
|
221339
221852
|
isolatedResults.push({
|
|
221340
221853
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
221341
221854
|
severity: "warning",
|
|
221342
221855
|
passed: false,
|
|
221343
|
-
message: `Module '${moduleSummary.module}' is
|
|
221856
|
+
message: `Module '${moduleSummary.module}' is a potential orphan: it has no declared or actual inbound/outbound module dependencies and little evidence of intentional architectural use.`,
|
|
221344
221857
|
findingType: "module-dependency",
|
|
221345
221858
|
module: moduleSummary.module
|
|
221346
221859
|
});
|
|
@@ -221353,7 +221866,7 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
221353
221866
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
221354
221867
|
severity: "warning",
|
|
221355
221868
|
passed: true,
|
|
221356
|
-
message: "No
|
|
221869
|
+
message: "No potential orphan modules detected among registered modules with existing registry paths."
|
|
221357
221870
|
}
|
|
221358
221871
|
];
|
|
221359
221872
|
}
|
|
@@ -222135,7 +222648,10 @@ function filterDependencyGraphSummaryByModuleScope(dependencyGraphSummary, modul
|
|
|
222135
222648
|
)
|
|
222136
222649
|
}));
|
|
222137
222650
|
return {
|
|
222138
|
-
modules: scopedModules
|
|
222651
|
+
modules: scopedModules,
|
|
222652
|
+
hierarchyEdges: dependencyGraphSummary.hierarchyEdges.filter(
|
|
222653
|
+
(edge) => scopeSet.has(edge.parentModule) && scopeSet.has(edge.childModule)
|
|
222654
|
+
)
|
|
222139
222655
|
};
|
|
222140
222656
|
}
|
|
222141
222657
|
async function runArchitectureValidationForWorkspace(workspaceRoot, options) {
|
|
@@ -225136,9 +225652,32 @@ async function readTextFileIfExists4(filePath) {
|
|
|
225136
225652
|
return void 0;
|
|
225137
225653
|
}
|
|
225138
225654
|
}
|
|
225655
|
+
var sourceFileExtensions2 = /* @__PURE__ */ new Set([
|
|
225656
|
+
".ts",
|
|
225657
|
+
".tsx",
|
|
225658
|
+
".js",
|
|
225659
|
+
".jsx",
|
|
225660
|
+
".mjs",
|
|
225661
|
+
".cjs",
|
|
225662
|
+
".mts",
|
|
225663
|
+
".cts",
|
|
225664
|
+
".py",
|
|
225665
|
+
".go",
|
|
225666
|
+
".java",
|
|
225667
|
+
".kt",
|
|
225668
|
+
".kts",
|
|
225669
|
+
".cs",
|
|
225670
|
+
".php",
|
|
225671
|
+
".rb",
|
|
225672
|
+
".vue",
|
|
225673
|
+
".svelte"
|
|
225674
|
+
]);
|
|
225139
225675
|
function sortUnique3(values) {
|
|
225140
225676
|
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
225141
225677
|
}
|
|
225678
|
+
function toSafeContractModuleFileStem2(moduleName) {
|
|
225679
|
+
return normalizePath5(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
225680
|
+
}
|
|
225142
225681
|
function getModuleRegistry(contract) {
|
|
225143
225682
|
const modules = contract?.modules;
|
|
225144
225683
|
if (!modules || typeof modules !== "object" || Array.isArray(modules)) {
|
|
@@ -225149,6 +225688,83 @@ function getModuleRegistry(contract) {
|
|
|
225149
225688
|
function normalizePublicEntrypointList(entries) {
|
|
225150
225689
|
return sortUnique3(entries.map((entry) => normalizePath5(entry.trim())).filter((entry) => entry.length > 0));
|
|
225151
225690
|
}
|
|
225691
|
+
function hasConfiguredPublicEntrypoint(registryEntry) {
|
|
225692
|
+
return Array.isArray(registryEntry?.publicEntrypoints) && registryEntry.publicEntrypoints.some(
|
|
225693
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
225694
|
+
);
|
|
225695
|
+
}
|
|
225696
|
+
async function readPackageJsonIfExists(moduleDirectoryPath) {
|
|
225697
|
+
const content = await readTextFileIfExists4(path26.join(moduleDirectoryPath, "package.json"));
|
|
225698
|
+
if (!content) {
|
|
225699
|
+
return void 0;
|
|
225700
|
+
}
|
|
225701
|
+
try {
|
|
225702
|
+
const parsed = JSON.parse(content);
|
|
225703
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
225704
|
+
} catch {
|
|
225705
|
+
return void 0;
|
|
225706
|
+
}
|
|
225707
|
+
}
|
|
225708
|
+
async function collectModuleSourceEvidence(directoryPath) {
|
|
225709
|
+
let entries;
|
|
225710
|
+
try {
|
|
225711
|
+
entries = await import_node_fs24.promises.readdir(directoryPath, { withFileTypes: true });
|
|
225712
|
+
} catch {
|
|
225713
|
+
return { sourceFileCount: 0, hasReadme: false, hasEntrypointFile: false };
|
|
225714
|
+
}
|
|
225715
|
+
let sourceFileCount = 0;
|
|
225716
|
+
let hasReadme = false;
|
|
225717
|
+
let hasEntrypointFile = false;
|
|
225718
|
+
for (const entry of entries) {
|
|
225719
|
+
const fullPath = path26.join(directoryPath, entry.name);
|
|
225720
|
+
if (entry.isDirectory()) {
|
|
225721
|
+
if (["node_modules", "dist", "out", "build", "coverage", ".git"].includes(entry.name)) {
|
|
225722
|
+
continue;
|
|
225723
|
+
}
|
|
225724
|
+
const nested = await collectModuleSourceEvidence(fullPath);
|
|
225725
|
+
sourceFileCount += nested.sourceFileCount;
|
|
225726
|
+
hasReadme ||= nested.hasReadme;
|
|
225727
|
+
hasEntrypointFile ||= nested.hasEntrypointFile;
|
|
225728
|
+
continue;
|
|
225729
|
+
}
|
|
225730
|
+
if (!entry.isFile()) {
|
|
225731
|
+
continue;
|
|
225732
|
+
}
|
|
225733
|
+
const lowerName = entry.name.toLowerCase();
|
|
225734
|
+
if (lowerName === "readme.md" || lowerName === "readme.mdx") {
|
|
225735
|
+
hasReadme = true;
|
|
225736
|
+
}
|
|
225737
|
+
if (/^(index|main|app|extension|cli)\.[cm]?[jt]sx?$/iu.test(entry.name)) {
|
|
225738
|
+
hasEntrypointFile = true;
|
|
225739
|
+
}
|
|
225740
|
+
if (sourceFileExtensions2.has(path26.extname(entry.name).toLowerCase())) {
|
|
225741
|
+
sourceFileCount += 1;
|
|
225742
|
+
}
|
|
225743
|
+
}
|
|
225744
|
+
return { sourceFileCount, hasReadme, hasEntrypointFile };
|
|
225745
|
+
}
|
|
225746
|
+
function hasKnownBoundaryRole(moduleName) {
|
|
225747
|
+
const normalized = moduleName.toLowerCase();
|
|
225748
|
+
const compact = normalized.replace(/[-_/]/gu, "");
|
|
225749
|
+
const segments = normalized.split(/[/-]/u);
|
|
225750
|
+
return ["adr", "ci", "compliance", "config", "governance", "policy", "validation", "smartinit"].some(
|
|
225751
|
+
(role) => compact.includes(role) || segments.includes(role)
|
|
225752
|
+
);
|
|
225753
|
+
}
|
|
225754
|
+
async function hasPackageEntrypoint(moduleDirectoryPath) {
|
|
225755
|
+
const packageJson = await readPackageJsonIfExists(moduleDirectoryPath);
|
|
225756
|
+
if (!packageJson) {
|
|
225757
|
+
return false;
|
|
225758
|
+
}
|
|
225759
|
+
return ["main", "module", "browser", "types", "bin", "exports"].some(
|
|
225760
|
+
(field) => packageJson[field] !== void 0
|
|
225761
|
+
);
|
|
225762
|
+
}
|
|
225763
|
+
async function hasIntentionalIndependentEvidence(args) {
|
|
225764
|
+
const moduleDirectoryPath = path26.join(args.workspaceRoot, ...args.moduleEntry.sourcePath.split("/"));
|
|
225765
|
+
const evidence = await collectModuleSourceEvidence(moduleDirectoryPath);
|
|
225766
|
+
return args.moduleEntry.contractExists || args.moduleEntry.hasPublicEntrypoint === true || evidence.hasReadme || evidence.hasEntrypointFile || await hasPackageEntrypoint(moduleDirectoryPath) || args.hasChildren || args.isLeafChild && evidence.sourceFileCount > 0 || hasKnownBoundaryRole(args.moduleEntry.moduleName) || evidence.sourceFileCount >= 2;
|
|
225767
|
+
}
|
|
225152
225768
|
async function loadModuleContractPublicEntrypoints(workspaceRoot, contractPath) {
|
|
225153
225769
|
const fullContractPath = path26.join(workspaceRoot, ...contractPath.split("/"));
|
|
225154
225770
|
const content = await readTextFileIfExists4(fullContractPath);
|
|
@@ -225189,7 +225805,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
225189
225805
|
const discovered = [];
|
|
225190
225806
|
const discoveredNames = /* @__PURE__ */ new Set();
|
|
225191
225807
|
const toContractPath = (moduleName, registryEntry) => normalizePath5(
|
|
225192
|
-
typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
|
|
225808
|
+
typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
|
|
225193
225809
|
);
|
|
225194
225810
|
const resolveMissingPublicEntrypointAlignment = async (registryEntry, contractPath) => {
|
|
225195
225811
|
if (!Array.isArray(registryEntry?.publicEntrypoints)) {
|
|
@@ -225233,7 +225849,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
225233
225849
|
continue;
|
|
225234
225850
|
}
|
|
225235
225851
|
const contractPath = normalizePath5(
|
|
225236
|
-
typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
|
|
225852
|
+
typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
|
|
225237
225853
|
);
|
|
225238
225854
|
const contractExists = await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")));
|
|
225239
225855
|
const missingPublicEntrypointAlignment = await resolveMissingPublicEntrypointAlignment(
|
|
@@ -225248,6 +225864,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
225248
225864
|
...resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) } : {},
|
|
225249
225865
|
contractPath,
|
|
225250
225866
|
contractExists,
|
|
225867
|
+
hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
|
|
225251
225868
|
...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
|
|
225252
225869
|
});
|
|
225253
225870
|
discoveredNames.add(moduleName);
|
|
@@ -225272,18 +225889,56 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
225272
225889
|
...resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) } : {},
|
|
225273
225890
|
contractPath,
|
|
225274
225891
|
contractExists,
|
|
225892
|
+
hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
|
|
225275
225893
|
...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
|
|
225276
225894
|
});
|
|
225277
225895
|
discoveredNames.add(moduleName);
|
|
225278
225896
|
}
|
|
225897
|
+
for (const moduleRoot of await discoverHierarchicalModuleRoots(workspaceRoot, normalizedModulesRoot)) {
|
|
225898
|
+
if (discoveredNames.has(moduleRoot.moduleId)) {
|
|
225899
|
+
continue;
|
|
225900
|
+
}
|
|
225901
|
+
const registryEntry = moduleRegistry[moduleRoot.moduleId];
|
|
225902
|
+
const contractPath = toContractPath(moduleRoot.moduleId, registryEntry);
|
|
225903
|
+
const contractExists = await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")));
|
|
225904
|
+
const missingPublicEntrypointAlignment = await resolveMissingPublicEntrypointAlignment(
|
|
225905
|
+
registryEntry,
|
|
225906
|
+
contractPath
|
|
225907
|
+
);
|
|
225908
|
+
discovered.push({
|
|
225909
|
+
moduleName: moduleRoot.moduleId,
|
|
225910
|
+
sourcePath: moduleRoot.sourcePath,
|
|
225911
|
+
discoverySource: "architecture.json",
|
|
225912
|
+
sourcePathExists: true,
|
|
225913
|
+
...resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath, contract) } : {},
|
|
225914
|
+
contractPath,
|
|
225915
|
+
contractExists,
|
|
225916
|
+
hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
|
|
225917
|
+
...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
|
|
225918
|
+
});
|
|
225919
|
+
discoveredNames.add(moduleRoot.moduleId);
|
|
225920
|
+
}
|
|
225279
225921
|
return discovered.sort((left, right) => left.moduleName.localeCompare(right.moduleName));
|
|
225280
225922
|
}
|
|
225281
225923
|
async function discoverModulesFromFallbackScan(workspaceRoot, modulesRoot = "src/modules", contractsRoot = ".archpilot/contracts") {
|
|
225282
|
-
const
|
|
225924
|
+
const hierarchicalModules = await discoverHierarchicalModuleRoots(workspaceRoot, modulesRoot);
|
|
225925
|
+
const moduleNames = hierarchicalModules.length > 0 ? [] : await listModuleDirectoryNames(workspaceRoot, modulesRoot);
|
|
225283
225926
|
const discovered = [];
|
|
225927
|
+
for (const moduleRoot of hierarchicalModules) {
|
|
225928
|
+
const contractPath = `${contractsRoot}/${toSafeContractModuleFileStem2(moduleRoot.moduleId)}.contract.json`;
|
|
225929
|
+
discovered.push({
|
|
225930
|
+
moduleName: moduleRoot.moduleId,
|
|
225931
|
+
sourcePath: moduleRoot.sourcePath,
|
|
225932
|
+
discoverySource: "inferred-scan",
|
|
225933
|
+
sourcePathExists: true,
|
|
225934
|
+
...resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath) ? { scope: resolveModuleScopeForPath(workspaceRoot, moduleRoot.sourcePath) } : {},
|
|
225935
|
+
contractPath,
|
|
225936
|
+
contractExists: await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")))
|
|
225937
|
+
});
|
|
225938
|
+
}
|
|
225284
225939
|
for (const moduleName of moduleNames) {
|
|
225285
225940
|
const sourcePath = `${modulesRoot}/${moduleName}`;
|
|
225286
|
-
const contractPath = `${contractsRoot}/${moduleName}.contract.json`;
|
|
225941
|
+
const contractPath = `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`;
|
|
225287
225942
|
discovered.push({
|
|
225288
225943
|
moduleName,
|
|
225289
225944
|
sourcePath,
|
|
@@ -225381,14 +226036,39 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225381
226036
|
moduleFilter: moduleIds
|
|
225382
226037
|
});
|
|
225383
226038
|
const edges = dependencySnapshot.edges.map((edge) => toArchitectureMapEdge(edge));
|
|
226039
|
+
const hierarchyEdges = dependencySnapshot.hierarchyEdges;
|
|
225384
226040
|
const parsedCycles = dependencySnapshot.cycles.map((cycle) => parseCycleString(cycle)).filter((cycle) => cycle.length > 1).sort((left, right) => left.join("->").localeCompare(right.join("->")));
|
|
225385
226041
|
const incomingMap = buildIncomingMap(modules, edges);
|
|
225386
226042
|
const outgoingMap = buildOutgoingMap(modules, edges);
|
|
225387
226043
|
const cycleMembership = new Set(parsedCycles.flatMap((cycle) => cycle));
|
|
225388
|
-
const
|
|
226044
|
+
const hierarchyParentByChild = new Map(
|
|
226045
|
+
hierarchyEdges.map((edge) => [edge.childModule, edge.parentModule])
|
|
226046
|
+
);
|
|
226047
|
+
const childModulesByParent = /* @__PURE__ */ new Map();
|
|
226048
|
+
for (const edge of hierarchyEdges) {
|
|
226049
|
+
const children = childModulesByParent.get(edge.parentModule) ?? [];
|
|
226050
|
+
children.push(edge.childModule);
|
|
226051
|
+
childModulesByParent.set(edge.parentModule, children);
|
|
226052
|
+
}
|
|
226053
|
+
const independentModuleNames = [];
|
|
226054
|
+
const potentialOrphanModuleNames = [];
|
|
226055
|
+
const hotspotSummary = await Promise.all(modules.map(async (moduleEntry) => {
|
|
225389
226056
|
const incomingDependencyCount = incomingMap.get(moduleEntry.moduleName)?.length ?? 0;
|
|
225390
226057
|
const outgoingDependencyCount = outgoingMap.get(moduleEntry.moduleName)?.length ?? 0;
|
|
225391
|
-
const
|
|
226058
|
+
const hasNoPeerUsage = incomingDependencyCount === 0 && outgoingDependencyCount === 0;
|
|
226059
|
+
const hasIntentionalEvidence = hasNoPeerUsage ? await hasIntentionalIndependentEvidence({
|
|
226060
|
+
workspaceRoot,
|
|
226061
|
+
moduleEntry,
|
|
226062
|
+
hasChildren: (childModulesByParent.get(moduleEntry.moduleName)?.length ?? 0) > 0,
|
|
226063
|
+
isLeafChild: hierarchyParentByChild.has(moduleEntry.moduleName) && (childModulesByParent.get(moduleEntry.moduleName)?.length ?? 0) === 0
|
|
226064
|
+
}) : false;
|
|
226065
|
+
const orphan = hasNoPeerUsage && !hasIntentionalEvidence;
|
|
226066
|
+
if (hasNoPeerUsage && hasIntentionalEvidence) {
|
|
226067
|
+
independentModuleNames.push(moduleEntry.moduleName);
|
|
226068
|
+
}
|
|
226069
|
+
if (orphan) {
|
|
226070
|
+
potentialOrphanModuleNames.push(moduleEntry.moduleName);
|
|
226071
|
+
}
|
|
225392
226072
|
return {
|
|
225393
226073
|
moduleName: moduleEntry.moduleName,
|
|
225394
226074
|
incomingDependencyCount,
|
|
@@ -225400,7 +226080,7 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225400
226080
|
missingPublicEntrypointAlignment: moduleEntry.missingPublicEntrypointAlignment
|
|
225401
226081
|
} : {}
|
|
225402
226082
|
};
|
|
225403
|
-
});
|
|
226083
|
+
}));
|
|
225404
226084
|
const mostDependedOnModules = sortRankedCounts(
|
|
225405
226085
|
hotspotSummary.filter((entry) => entry.incomingDependencyCount > 0).map((entry) => ({
|
|
225406
226086
|
moduleName: entry.moduleName,
|
|
@@ -225416,9 +226096,9 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225416
226096
|
const modulesInCircularDependencies = sortUnique3(
|
|
225417
226097
|
hotspotSummary.filter((entry) => entry.participatesInCycle).map((entry) => entry.moduleName)
|
|
225418
226098
|
);
|
|
225419
|
-
const
|
|
225420
|
-
|
|
225421
|
-
|
|
226099
|
+
const independentModules = sortUnique3(independentModuleNames);
|
|
226100
|
+
const potentialOrphanModules = sortUnique3(potentialOrphanModuleNames);
|
|
226101
|
+
const orphanModules = potentialOrphanModules;
|
|
225422
226102
|
const missingContracts = sortUnique3(
|
|
225423
226103
|
hotspotSummary.filter((entry) => entry.missingContract).map((entry) => entry.moduleName)
|
|
225424
226104
|
);
|
|
@@ -225441,11 +226121,14 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225441
226121
|
modules,
|
|
225442
226122
|
moduleHotspotSummary: hotspotSummary,
|
|
225443
226123
|
edges,
|
|
226124
|
+
hierarchyEdges,
|
|
225444
226125
|
cycles: parsedCycles,
|
|
225445
226126
|
hotspots: {
|
|
225446
226127
|
mostDependedOnModules,
|
|
225447
226128
|
highestOutgoingFanOutModules,
|
|
225448
226129
|
modulesInCircularDependencies,
|
|
226130
|
+
independentModules,
|
|
226131
|
+
potentialOrphanModules,
|
|
225449
226132
|
orphanModules,
|
|
225450
226133
|
missingContracts,
|
|
225451
226134
|
highlyConnectedModules,
|
|
@@ -225455,6 +226138,8 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225455
226138
|
moduleCount: modules.length,
|
|
225456
226139
|
edgeCount: edges.length,
|
|
225457
226140
|
cycleCount: parsedCycles.length,
|
|
226141
|
+
independentCount: independentModules.length,
|
|
226142
|
+
potentialOrphanCount: potentialOrphanModules.length,
|
|
225458
226143
|
orphanCount: orphanModules.length,
|
|
225459
226144
|
highlyConnectedCount: highlyConnectedModules.length,
|
|
225460
226145
|
missingContractCount: missingContracts.length
|
|
@@ -225470,6 +226155,11 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225470
226155
|
importCount: edge.importCount,
|
|
225471
226156
|
publicImportCount: edge.publicImportCount,
|
|
225472
226157
|
nonPublicImportCount: edge.nonPublicImportCount
|
|
226158
|
+
})),
|
|
226159
|
+
hierarchyEdges: hierarchyEdges.map((edge) => ({
|
|
226160
|
+
source: edge.parentModule,
|
|
226161
|
+
target: edge.childModule,
|
|
226162
|
+
relationship: "contains"
|
|
225473
226163
|
}))
|
|
225474
226164
|
},
|
|
225475
226165
|
notes: {
|
|
@@ -230500,6 +231190,9 @@ async function pathExists13(targetPath) {
|
|
|
230500
231190
|
function sortUnique12(values) {
|
|
230501
231191
|
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
230502
231192
|
}
|
|
231193
|
+
function toSafeContractModuleFileStem3(moduleName) {
|
|
231194
|
+
return normalizePath11(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
231195
|
+
}
|
|
230503
231196
|
function wildcardToRegex(pattern) {
|
|
230504
231197
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
230505
231198
|
return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "u");
|
|
@@ -230581,7 +231274,10 @@ function resolveContractPath(contract, moduleName) {
|
|
|
230581
231274
|
return normalizePath11(configured);
|
|
230582
231275
|
}
|
|
230583
231276
|
return normalizePath11(
|
|
230584
|
-
path44.posix.join(
|
|
231277
|
+
path44.posix.join(
|
|
231278
|
+
resolveContractsRootFromContract(contract),
|
|
231279
|
+
`${toSafeContractModuleFileStem3(moduleName)}.contract.json`
|
|
231280
|
+
)
|
|
230585
231281
|
);
|
|
230586
231282
|
}
|
|
230587
231283
|
function createModuleContract(input2) {
|
|
@@ -230593,6 +231289,23 @@ function createModuleContract(input2) {
|
|
|
230593
231289
|
exposedApis: []
|
|
230594
231290
|
};
|
|
230595
231291
|
}
|
|
231292
|
+
async function readExistingModuleContractPublicEntrypoints(workspaceRoot, contractPath) {
|
|
231293
|
+
try {
|
|
231294
|
+
const raw = await import_node_fs39.promises.readFile(toAbsolutePath(workspaceRoot, contractPath), {
|
|
231295
|
+
encoding: "utf8"
|
|
231296
|
+
});
|
|
231297
|
+
const parsed = JSON.parse(raw);
|
|
231298
|
+
if (!Array.isArray(parsed.publicEntrypoints)) {
|
|
231299
|
+
return void 0;
|
|
231300
|
+
}
|
|
231301
|
+
if (parsed.publicEntrypoints.some((entry) => typeof entry !== "string")) {
|
|
231302
|
+
return void 0;
|
|
231303
|
+
}
|
|
231304
|
+
return sortUnique12(parsed.publicEntrypoints.map((entry) => normalizePath11(entry)));
|
|
231305
|
+
} catch {
|
|
231306
|
+
return void 0;
|
|
231307
|
+
}
|
|
231308
|
+
}
|
|
230596
231309
|
function isRegistryEntryDifferent(input2) {
|
|
230597
231310
|
const moduleRegistry = getModuleRegistry2(input2.contract);
|
|
230598
231311
|
const existing = moduleRegistry[input2.moduleName] ?? {};
|
|
@@ -230619,7 +231332,15 @@ async function generateMissingModuleContracts(workspaceRoot) {
|
|
|
230619
231332
|
)) {
|
|
230620
231333
|
const contractPath = resolveContractPath(contract, moduleEntry.moduleName);
|
|
230621
231334
|
const sourcePath = normalizePath11(moduleEntry.sourcePath);
|
|
230622
|
-
const
|
|
231335
|
+
const absoluteContractPath = toAbsolutePath(workspaceRoot, contractPath);
|
|
231336
|
+
const contractExists = await pathExists13(absoluteContractPath);
|
|
231337
|
+
const existingContractPublicEntrypoints = contractExists ? await readExistingModuleContractPublicEntrypoints(workspaceRoot, contractPath) : void 0;
|
|
231338
|
+
const configuredPublicEntrypoints = !contractExists && Array.isArray(getModuleRegistry2(contract)[moduleEntry.moduleName]?.publicEntrypoints) ? sortUnique12(
|
|
231339
|
+
getModuleRegistry2(contract)[moduleEntry.moduleName]?.publicEntrypoints?.map(
|
|
231340
|
+
(entry) => normalizePath11(entry)
|
|
231341
|
+
) ?? []
|
|
231342
|
+
) : void 0;
|
|
231343
|
+
const publicEntrypoints = existingContractPublicEntrypoints ?? configuredPublicEntrypoints ?? await inferModulePublicEntrypoints(
|
|
230623
231344
|
workspaceRoot,
|
|
230624
231345
|
sourcePath,
|
|
230625
231346
|
adapter
|
|
@@ -230629,8 +231350,7 @@ async function generateMissingModuleContracts(workspaceRoot) {
|
|
|
230629
231350
|
dependsOn: dependsOnBySource.get(moduleEntry.moduleName) ?? [],
|
|
230630
231351
|
publicEntrypoints
|
|
230631
231352
|
});
|
|
230632
|
-
|
|
230633
|
-
if (await pathExists13(absoluteContractPath)) {
|
|
231353
|
+
if (contractExists) {
|
|
230634
231354
|
skippedExistingContracts.push(contractPath);
|
|
230635
231355
|
} else {
|
|
230636
231356
|
await import_node_fs39.promises.mkdir(path44.dirname(absoluteContractPath), { recursive: true });
|
|
@@ -232776,7 +233496,7 @@ var appRouteReservedDirectoryNames = /* @__PURE__ */ new Set([
|
|
|
232776
233496
|
"common",
|
|
232777
233497
|
"shared-ui"
|
|
232778
233498
|
]);
|
|
232779
|
-
var
|
|
233499
|
+
var sourceFileExtensions3 = /* @__PURE__ */ new Set([
|
|
232780
233500
|
".ts",
|
|
232781
233501
|
".tsx",
|
|
232782
233502
|
".js",
|
|
@@ -232894,7 +233614,7 @@ function inspectModuleFolder(absolutePath) {
|
|
|
232894
233614
|
continue;
|
|
232895
233615
|
}
|
|
232896
233616
|
const extension = path53.extname(entry.name).toLowerCase();
|
|
232897
|
-
if (
|
|
233617
|
+
if (sourceFileExtensions3.has(extension)) {
|
|
232898
233618
|
hasSourceFiles = true;
|
|
232899
233619
|
sourceFileCount += 1;
|
|
232900
233620
|
}
|
|
@@ -232974,14 +233694,17 @@ function collectModuleCandidatesFromRoot(workspaceRoot, modulesRootRelative, def
|
|
|
232974
233694
|
if (skipFrontendUtilities && frontendUtilityDirectoryNames.has(entry.name.toLowerCase())) {
|
|
232975
233695
|
continue;
|
|
232976
233696
|
}
|
|
232977
|
-
const
|
|
232978
|
-
const
|
|
233697
|
+
const rootSegments = modulesRootRelative.split("/").filter((segment) => segment.length > 0);
|
|
233698
|
+
const sourceIndex = rootSegments.lastIndexOf("src");
|
|
233699
|
+
const rootLeaf = rootSegments[rootSegments.length - 1]?.toLowerCase();
|
|
233700
|
+
const parentName = sourceIndex > 0 && (rootLeaf === "src" || ["modules", "features"].includes(rootLeaf ?? "")) ? rootSegments[sourceIndex - 1] : void 0;
|
|
233701
|
+
const moduleName = parentName && parentName !== "src" ? `${parentName}/${entry.name}` : ["domain", "domains", "application", "infrastructure"].includes(rootLeaf ?? "") ? `${rootSegments[rootSegments.length - 1]}/${entry.name}` : entry.name;
|
|
232979
233702
|
const moduleAbsolutePath = path53.join(absoluteRoot, entry.name);
|
|
232980
233703
|
const inspected = inspectModuleFolder(moduleAbsolutePath);
|
|
232981
233704
|
const detected = makeDetectedModule(
|
|
232982
233705
|
workspaceRoot,
|
|
232983
233706
|
moduleName,
|
|
232984
|
-
|
|
233707
|
+
normalizePath13(`${modulesRootRelative}/${entry.name}`),
|
|
232985
233708
|
modulesRootRelative,
|
|
232986
233709
|
inspected.hasSourceFiles,
|
|
232987
233710
|
inspected.hasRoleFiles,
|
|
@@ -233042,6 +233765,10 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
233042
233765
|
for (const root of [
|
|
233043
233766
|
"src/modules",
|
|
233044
233767
|
"src/features",
|
|
233768
|
+
"src/domain",
|
|
233769
|
+
"src/domains",
|
|
233770
|
+
"src/application",
|
|
233771
|
+
"src/infrastructure",
|
|
233045
233772
|
"src/app",
|
|
233046
233773
|
"app",
|
|
233047
233774
|
"backend/src/modules",
|
|
@@ -233068,8 +233795,13 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
233068
233795
|
continue;
|
|
233069
233796
|
}
|
|
233070
233797
|
for (const candidate of [
|
|
233798
|
+
`apps/${appEntry.name}/src`,
|
|
233071
233799
|
`apps/${appEntry.name}/src/modules`,
|
|
233072
233800
|
`apps/${appEntry.name}/src/features`,
|
|
233801
|
+
`apps/${appEntry.name}/src/domain`,
|
|
233802
|
+
`apps/${appEntry.name}/src/domains`,
|
|
233803
|
+
`apps/${appEntry.name}/src/application`,
|
|
233804
|
+
`apps/${appEntry.name}/src/infrastructure`,
|
|
233073
233805
|
`apps/${appEntry.name}/app`,
|
|
233074
233806
|
`apps/${appEntry.name}/src/app`
|
|
233075
233807
|
]) {
|
|
@@ -233078,6 +233810,27 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
233078
233810
|
}
|
|
233079
233811
|
}
|
|
233080
233812
|
}
|
|
233813
|
+
for (const container of ["packages", "services"]) {
|
|
233814
|
+
const containerRoot = path53.join(workspaceRoot, container);
|
|
233815
|
+
for (const containerEntry of safeReadDirEntries(containerRoot)) {
|
|
233816
|
+
if (!containerEntry.isDirectory() || ignoredDirectoryNames4.has(containerEntry.name)) {
|
|
233817
|
+
continue;
|
|
233818
|
+
}
|
|
233819
|
+
for (const candidate of [
|
|
233820
|
+
`${container}/${containerEntry.name}/src`,
|
|
233821
|
+
`${container}/${containerEntry.name}/src/modules`,
|
|
233822
|
+
`${container}/${containerEntry.name}/src/features`,
|
|
233823
|
+
`${container}/${containerEntry.name}/src/domain`,
|
|
233824
|
+
`${container}/${containerEntry.name}/src/domains`,
|
|
233825
|
+
`${container}/${containerEntry.name}/src/application`,
|
|
233826
|
+
`${container}/${containerEntry.name}/src/infrastructure`
|
|
233827
|
+
]) {
|
|
233828
|
+
if (pathExists15(path53.join(workspaceRoot, ...candidate.split("/")))) {
|
|
233829
|
+
roots.push(candidate);
|
|
233830
|
+
}
|
|
233831
|
+
}
|
|
233832
|
+
}
|
|
233833
|
+
}
|
|
233081
233834
|
return uniqueSorted(roots);
|
|
233082
233835
|
}
|
|
233083
233836
|
function collectJavaPackageRoots(workspaceRoot) {
|