@archpilotlabs/archpilot 0.0.8 → 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/README.md +41 -2
- package/dist/archpilot-cli.js +1302 -99
- package/package.json +34 -34
package/dist/archpilot-cli.js
CHANGED
|
@@ -210932,6 +210932,7 @@ var import_node_fs = require("node:fs");
|
|
|
210932
210932
|
var validProjectKinds = /* @__PURE__ */ new Set(["backend", "frontend", "library"]);
|
|
210933
210933
|
var validCanonicalStackIds = /* @__PURE__ */ new Set([
|
|
210934
210934
|
"node_typescript",
|
|
210935
|
+
"express",
|
|
210935
210936
|
"nestjs",
|
|
210936
210937
|
"java",
|
|
210937
210938
|
"spring",
|
|
@@ -210940,9 +210941,11 @@ var validCanonicalStackIds = /* @__PURE__ */ new Set([
|
|
|
210940
210941
|
"flask",
|
|
210941
210942
|
"python",
|
|
210942
210943
|
"php",
|
|
210944
|
+
"laravel",
|
|
210943
210945
|
"go",
|
|
210944
210946
|
"dotnet",
|
|
210945
210947
|
"ruby_rails",
|
|
210948
|
+
"kotlin",
|
|
210946
210949
|
"kotlin_spring",
|
|
210947
210950
|
"rust",
|
|
210948
210951
|
"react",
|
|
@@ -210956,6 +210959,7 @@ var validCanonicalStackIds = /* @__PURE__ */ new Set([
|
|
|
210956
210959
|
]);
|
|
210957
210960
|
var canonicalStackLabels = {
|
|
210958
210961
|
node_typescript: "TypeScript / Node.js",
|
|
210962
|
+
express: "Express.js",
|
|
210959
210963
|
nestjs: "NestJS",
|
|
210960
210964
|
java: "Java",
|
|
210961
210965
|
spring: "Spring",
|
|
@@ -210964,9 +210968,11 @@ var canonicalStackLabels = {
|
|
|
210964
210968
|
flask: "Flask",
|
|
210965
210969
|
python: "Python",
|
|
210966
210970
|
php: "PHP",
|
|
210971
|
+
laravel: "Laravel",
|
|
210967
210972
|
go: "Go",
|
|
210968
210973
|
dotnet: ".NET",
|
|
210969
210974
|
ruby_rails: "Ruby / Rails",
|
|
210975
|
+
kotlin: "Kotlin",
|
|
210970
210976
|
kotlin_spring: "Kotlin / Spring",
|
|
210971
210977
|
rust: "Rust",
|
|
210972
210978
|
react: "React",
|
|
@@ -211177,6 +211183,103 @@ var nodeTypescriptAdapter = buildAdapter({
|
|
|
211177
211183
|
var stackAdaptersById = {
|
|
211178
211184
|
other: genericDefaultAdapter,
|
|
211179
211185
|
node_typescript: nodeTypescriptAdapter,
|
|
211186
|
+
express: buildAdapter({
|
|
211187
|
+
id: "express",
|
|
211188
|
+
displayName: canonicalStackLabels.express,
|
|
211189
|
+
ecosystem: "node",
|
|
211190
|
+
category: "framework",
|
|
211191
|
+
priority: 70,
|
|
211192
|
+
manifestDetectionHints: ["package.json", "tsconfig.json", "app.js", "app.ts", "server.js", "server.ts"],
|
|
211193
|
+
buildToolHints: ["npm", "pnpm", "yarn"],
|
|
211194
|
+
defaults: {
|
|
211195
|
+
sourceRoots: ["src", "."],
|
|
211196
|
+
moduleRootCandidates: ["src/modules", "src/features", "routes", "src/routes", "src"],
|
|
211197
|
+
apiRoots: ["routes", "src/routes", "src/api", "api"],
|
|
211198
|
+
databaseRoots: ["prisma", "db", "database", "migrations"],
|
|
211199
|
+
configFiles: ["package.json", "tsconfig.json"],
|
|
211200
|
+
fileExtensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
|
|
211201
|
+
dependencyFileExtensions: [".ts", ".js", ".mjs", ".cjs"],
|
|
211202
|
+
ignoreDirectories: ["node_modules", "dist", "coverage"],
|
|
211203
|
+
entrypointHints: ["app.ts", "server.ts", "src/app.ts", "src/server.ts", "app.js", "server.js", "src/app.js", "src/server.js"],
|
|
211204
|
+
publicEntrypointDirectories: ["public"],
|
|
211205
|
+
moduleIndexFileName: "index.ts"
|
|
211206
|
+
},
|
|
211207
|
+
detect: (context) => {
|
|
211208
|
+
const evidence = [];
|
|
211209
|
+
let strongSignals = 0;
|
|
211210
|
+
let mediumSignals = 0;
|
|
211211
|
+
let score = 0;
|
|
211212
|
+
const hasExpressDependency = context.hasPackageDependency("express");
|
|
211213
|
+
const hasExpressFactory = context.hasAnyFileMatchingPattern(
|
|
211214
|
+
/\.(ts|js|mjs|cjs)$/iu,
|
|
211215
|
+
/\bexpress\s*\(\s*\)/u
|
|
211216
|
+
);
|
|
211217
|
+
const hasExpressImport = context.hasAnyFileMatchingPattern(
|
|
211218
|
+
/\.(ts|js|mjs|cjs)$/iu,
|
|
211219
|
+
/\b(import\s+express\s+from\s+['"]express['"]|require\s*\(\s*['"]express['"]\s*\))/u
|
|
211220
|
+
);
|
|
211221
|
+
const hasAppUse = context.hasAnyFileMatchingPattern(
|
|
211222
|
+
/\.(ts|js|mjs|cjs)$/iu,
|
|
211223
|
+
/\bapp\.use\s*\(/u
|
|
211224
|
+
);
|
|
211225
|
+
const hasRouterUsage = context.hasAnyFileMatchingPattern(
|
|
211226
|
+
/\.(ts|js|mjs|cjs)$/iu,
|
|
211227
|
+
/\b(router|Router)\s*\.\s*(get|post|put|patch|delete|use)\s*\(/u
|
|
211228
|
+
);
|
|
211229
|
+
const hasRoutesDirectory = context.hasDirectory("routes") || context.hasDirectory("src/routes");
|
|
211230
|
+
const hasRouteFiles = context.hasAnyPathMatchingPattern(/(^|\/)(routes|src\/routes)\/.+\.(ts|js|mjs|cjs)$/iu);
|
|
211231
|
+
if (hasExpressDependency) {
|
|
211232
|
+
strongSignals += 1;
|
|
211233
|
+
score += 46;
|
|
211234
|
+
evidence.push("Found package.json with express dependency");
|
|
211235
|
+
}
|
|
211236
|
+
if (hasExpressFactory) {
|
|
211237
|
+
strongSignals += 1;
|
|
211238
|
+
score += 28;
|
|
211239
|
+
evidence.push("Found express() app construction");
|
|
211240
|
+
}
|
|
211241
|
+
if (hasExpressImport) {
|
|
211242
|
+
mediumSignals += 1;
|
|
211243
|
+
score += 12;
|
|
211244
|
+
evidence.push("Found Express import or require");
|
|
211245
|
+
}
|
|
211246
|
+
if (hasAppUse) {
|
|
211247
|
+
mediumSignals += 1;
|
|
211248
|
+
score += 10;
|
|
211249
|
+
evidence.push("Found app.use(...) middleware or router mounting");
|
|
211250
|
+
}
|
|
211251
|
+
if (hasRouterUsage) {
|
|
211252
|
+
mediumSignals += 1;
|
|
211253
|
+
score += 10;
|
|
211254
|
+
evidence.push("Found router HTTP method usage");
|
|
211255
|
+
}
|
|
211256
|
+
if (hasRoutesDirectory) {
|
|
211257
|
+
mediumSignals += 1;
|
|
211258
|
+
score += 6;
|
|
211259
|
+
evidence.push("Found routes/ or src/routes/ directory");
|
|
211260
|
+
}
|
|
211261
|
+
if (hasRouteFiles) {
|
|
211262
|
+
mediumSignals += 1;
|
|
211263
|
+
score += 5;
|
|
211264
|
+
evidence.push("Found route files under routes/");
|
|
211265
|
+
}
|
|
211266
|
+
const hasSafeExpressEvidence = hasExpressDependency && (hasExpressFactory || hasExpressImport || hasAppUse || hasRouterUsage || hasRoutesDirectory);
|
|
211267
|
+
if (!hasSafeExpressEvidence) {
|
|
211268
|
+
return null;
|
|
211269
|
+
}
|
|
211270
|
+
const confidence = strongSignals >= 2 || strongSignals >= 1 && mediumSignals >= 2 ? "high" : strongSignals >= 1 ? "medium" : "low";
|
|
211271
|
+
return toResult({
|
|
211272
|
+
adapterId: "express",
|
|
211273
|
+
confidence,
|
|
211274
|
+
score,
|
|
211275
|
+
evidence,
|
|
211276
|
+
projectKindHints: ["backend"],
|
|
211277
|
+
apiStyleHint: "rest"
|
|
211278
|
+
});
|
|
211279
|
+
},
|
|
211280
|
+
resolveProjectKinds: () => ["backend"],
|
|
211281
|
+
getImportExtractionMode: () => "typescript-imports"
|
|
211282
|
+
}),
|
|
211180
211283
|
nestjs: buildAdapter({
|
|
211181
211284
|
id: "nestjs",
|
|
211182
211285
|
displayName: canonicalStackLabels.nestjs,
|
|
@@ -211452,9 +211555,9 @@ var stackAdaptersById = {
|
|
|
211452
211555
|
],
|
|
211453
211556
|
buildToolHints: ["maven", "gradle"],
|
|
211454
211557
|
defaults: {
|
|
211455
|
-
sourceRoots: ["src/main/java", "src"],
|
|
211456
|
-
moduleRootCandidates: ["src/main/java"],
|
|
211457
|
-
apiRoots: ["src/main/java"],
|
|
211558
|
+
sourceRoots: ["src/main/java", "src/main/kotlin", "src"],
|
|
211559
|
+
moduleRootCandidates: ["src/main/java", "src/main/kotlin"],
|
|
211560
|
+
apiRoots: ["src/main/java", "src/main/kotlin"],
|
|
211458
211561
|
databaseRoots: ["src/main/resources", "src/main/resources/db"],
|
|
211459
211562
|
configFiles: [
|
|
211460
211563
|
"pom.xml",
|
|
@@ -211474,7 +211577,7 @@ var stackAdaptersById = {
|
|
|
211474
211577
|
"out",
|
|
211475
211578
|
...commonIgnoreDirectories
|
|
211476
211579
|
],
|
|
211477
|
-
entrypointHints: ["src/main/java"],
|
|
211580
|
+
entrypointHints: ["src/main/java", "src/main/kotlin", "Application.java", "Application.kt"],
|
|
211478
211581
|
publicEntrypointDirectories: [],
|
|
211479
211582
|
moduleIndexFileName: "Application.java"
|
|
211480
211583
|
},
|
|
@@ -211500,7 +211603,7 @@ var stackAdaptersById = {
|
|
|
211500
211603
|
/\.(java|kt)$/iu,
|
|
211501
211604
|
/@SpringBootApplication\b/u
|
|
211502
211605
|
);
|
|
211503
|
-
const
|
|
211606
|
+
const hasMainSourceWithDependencies = (context.hasDirectory("src/main/java") || context.hasDirectory("src/main/kotlin")) && hasSpringDependencyEvidence;
|
|
211504
211607
|
const hasApplicationConfigWithDependencies = (context.hasFile("application.yml") || context.hasFile("application.yaml") || context.hasFile("application.properties")) && hasSpringDependencyEvidence;
|
|
211505
211608
|
const hasRestController = context.hasAnyFileMatchingPattern(
|
|
211506
211609
|
/\.(java|kt)$/iu,
|
|
@@ -211524,9 +211627,9 @@ var stackAdaptersById = {
|
|
|
211524
211627
|
);
|
|
211525
211628
|
const hasResourcesDirectory = context.hasDirectory("src/main/resources");
|
|
211526
211629
|
const hasWrapperWithSpringEvidence = (context.hasFile("mvnw") || context.hasFile("gradlew")) && hasSpringDependencyEvidence;
|
|
211527
|
-
const
|
|
211630
|
+
const hasTestJvmSource = context.hasDirectory("src/test/java") || context.hasDirectory("src/test/kotlin");
|
|
211528
211631
|
const hasBootstrapConfig = context.hasFile("bootstrap.yml") || context.hasFile("bootstrap.yaml");
|
|
211529
|
-
const
|
|
211632
|
+
const hasJvmPackageShape = context.hasDirectory("src/main/java/com") || context.hasDirectory("src/main/java/org") || context.hasDirectory("src/main/kotlin/com") || context.hasDirectory("src/main/kotlin/org");
|
|
211530
211633
|
if (hasPomSpringDependency) {
|
|
211531
211634
|
strongSignals += 1;
|
|
211532
211635
|
score += 54;
|
|
@@ -211542,10 +211645,10 @@ var stackAdaptersById = {
|
|
|
211542
211645
|
score += 44;
|
|
211543
211646
|
evidence.push("Found @SpringBootApplication in source");
|
|
211544
211647
|
}
|
|
211545
|
-
if (
|
|
211648
|
+
if (hasMainSourceWithDependencies) {
|
|
211546
211649
|
strongSignals += 1;
|
|
211547
211650
|
score += 20;
|
|
211548
|
-
evidence.push("Found
|
|
211651
|
+
evidence.push("Found JVM main source root with Spring dependency evidence");
|
|
211549
211652
|
}
|
|
211550
211653
|
if (hasApplicationConfigWithDependencies) {
|
|
211551
211654
|
strongSignals += 1;
|
|
@@ -211587,22 +211690,22 @@ var stackAdaptersById = {
|
|
|
211587
211690
|
score += 4;
|
|
211588
211691
|
evidence.push("Found Maven/Gradle wrapper with Spring dependency evidence");
|
|
211589
211692
|
}
|
|
211590
|
-
if (
|
|
211693
|
+
if (hasTestJvmSource) {
|
|
211591
211694
|
weakSignals += 1;
|
|
211592
211695
|
score += 2;
|
|
211593
|
-
evidence.push("Found
|
|
211696
|
+
evidence.push("Found JVM test source directory");
|
|
211594
211697
|
}
|
|
211595
211698
|
if (hasBootstrapConfig) {
|
|
211596
211699
|
weakSignals += 1;
|
|
211597
211700
|
score += 2;
|
|
211598
211701
|
evidence.push("Found bootstrap.yml or bootstrap.yaml");
|
|
211599
211702
|
}
|
|
211600
|
-
if (
|
|
211703
|
+
if (hasJvmPackageShape) {
|
|
211601
211704
|
weakSignals += 1;
|
|
211602
211705
|
score += 1;
|
|
211603
|
-
evidence.push("Found conventional
|
|
211706
|
+
evidence.push("Found conventional JVM package directory under main source root");
|
|
211604
211707
|
}
|
|
211605
|
-
const hasSafeSpringCoreEvidence = hasSpringDependencyEvidence || hasSpringBootApplication || (hasRestController || hasController || hasService || hasRepository || hasMappingAnnotation) && (context.hasDirectory("src/main/java") || hasResourcesDirectory);
|
|
211708
|
+
const hasSafeSpringCoreEvidence = hasSpringDependencyEvidence || hasSpringBootApplication || (hasRestController || hasController || hasService || hasRepository || hasMappingAnnotation) && (context.hasDirectory("src/main/java") || context.hasDirectory("src/main/kotlin") || hasResourcesDirectory);
|
|
211606
211709
|
if (!hasSafeSpringCoreEvidence || strongSignals === 0) {
|
|
211607
211710
|
return null;
|
|
211608
211711
|
}
|
|
@@ -212191,6 +212294,101 @@ var stackAdaptersById = {
|
|
|
212191
212294
|
resolveProjectKinds: () => ["backend", "library"],
|
|
212192
212295
|
getImportExtractionMode: () => "python-imports"
|
|
212193
212296
|
}),
|
|
212297
|
+
laravel: buildAdapter({
|
|
212298
|
+
id: "laravel",
|
|
212299
|
+
displayName: canonicalStackLabels.laravel,
|
|
212300
|
+
ecosystem: "php",
|
|
212301
|
+
category: "framework",
|
|
212302
|
+
priority: 70,
|
|
212303
|
+
manifestDetectionHints: ["composer.json", "artisan", "web.php", "api.php", "app.php"],
|
|
212304
|
+
buildToolHints: ["composer", "artisan"],
|
|
212305
|
+
defaults: {
|
|
212306
|
+
sourceRoots: ["app", "routes", "config"],
|
|
212307
|
+
moduleRootCandidates: ["app/Modules", "app/Services", "app/Http/Controllers", "app"],
|
|
212308
|
+
apiRoots: ["routes", "app/Http/Controllers"],
|
|
212309
|
+
databaseRoots: ["database", "database/migrations"],
|
|
212310
|
+
configFiles: ["composer.json", "artisan", "config/app.php"],
|
|
212311
|
+
fileExtensions: [".php"],
|
|
212312
|
+
dependencyFileExtensions: [".php"],
|
|
212313
|
+
ignoreDirectories: [
|
|
212314
|
+
"vendor",
|
|
212315
|
+
"storage",
|
|
212316
|
+
"bootstrap/cache",
|
|
212317
|
+
"public",
|
|
212318
|
+
...commonIgnoreDirectories
|
|
212319
|
+
],
|
|
212320
|
+
entrypointHints: ["artisan", "public/index.php", "bootstrap/app.php"],
|
|
212321
|
+
publicEntrypointDirectories: [],
|
|
212322
|
+
moduleIndexFileName: "index.php"
|
|
212323
|
+
},
|
|
212324
|
+
detect: (context) => {
|
|
212325
|
+
const evidence = [];
|
|
212326
|
+
let strongSignals = 0;
|
|
212327
|
+
let mediumSignals = 0;
|
|
212328
|
+
let score = 0;
|
|
212329
|
+
const hasLaravelComposer = context.hasFileMatchingPattern(
|
|
212330
|
+
"composer.json",
|
|
212331
|
+
/["']laravel\/framework["']/iu
|
|
212332
|
+
);
|
|
212333
|
+
const hasArtisan = context.hasFile("artisan");
|
|
212334
|
+
const hasRoutesWeb = context.hasAnyPathMatchingPattern(/(^|\/)routes\/web\.php$/iu);
|
|
212335
|
+
const hasRoutesApi = context.hasAnyPathMatchingPattern(/(^|\/)routes\/api\.php$/iu);
|
|
212336
|
+
const hasControllers = context.hasDirectory("app/Http/Controllers");
|
|
212337
|
+
const hasConfigApp = context.hasAnyPathMatchingPattern(/(^|\/)config\/app\.php$/iu);
|
|
212338
|
+
const hasLaravelRouteFacade = context.hasAnyFileMatchingPattern(
|
|
212339
|
+
/(^|\/)routes\/.+\.php$/iu,
|
|
212340
|
+
/\bRoute::(get|post|put|patch|delete|middleware|group)\s*\(/u
|
|
212341
|
+
);
|
|
212342
|
+
if (hasLaravelComposer) {
|
|
212343
|
+
strongSignals += 1;
|
|
212344
|
+
score += 50;
|
|
212345
|
+
evidence.push("Found composer.json with laravel/framework dependency");
|
|
212346
|
+
}
|
|
212347
|
+
if (hasArtisan) {
|
|
212348
|
+
strongSignals += 1;
|
|
212349
|
+
score += 18;
|
|
212350
|
+
evidence.push("Found artisan entrypoint");
|
|
212351
|
+
}
|
|
212352
|
+
if (hasRoutesWeb) {
|
|
212353
|
+
mediumSignals += 1;
|
|
212354
|
+
score += 10;
|
|
212355
|
+
evidence.push("Found routes/web.php");
|
|
212356
|
+
}
|
|
212357
|
+
if (hasRoutesApi) {
|
|
212358
|
+
mediumSignals += 1;
|
|
212359
|
+
score += 10;
|
|
212360
|
+
evidence.push("Found routes/api.php");
|
|
212361
|
+
}
|
|
212362
|
+
if (hasControllers) {
|
|
212363
|
+
mediumSignals += 1;
|
|
212364
|
+
score += 10;
|
|
212365
|
+
evidence.push("Found app/Http/Controllers directory");
|
|
212366
|
+
}
|
|
212367
|
+
if (hasConfigApp) {
|
|
212368
|
+
mediumSignals += 1;
|
|
212369
|
+
score += 6;
|
|
212370
|
+
evidence.push("Found config/app.php");
|
|
212371
|
+
}
|
|
212372
|
+
if (hasLaravelRouteFacade) {
|
|
212373
|
+
mediumSignals += 1;
|
|
212374
|
+
score += 8;
|
|
212375
|
+
evidence.push("Found Laravel Route facade usage");
|
|
212376
|
+
}
|
|
212377
|
+
if (!hasLaravelComposer || !(hasArtisan || hasRoutesWeb || hasRoutesApi || hasControllers)) {
|
|
212378
|
+
return null;
|
|
212379
|
+
}
|
|
212380
|
+
return toResult({
|
|
212381
|
+
adapterId: "laravel",
|
|
212382
|
+
confidence: strongSignals >= 2 || mediumSignals >= 2 ? "high" : "medium",
|
|
212383
|
+
score,
|
|
212384
|
+
evidence,
|
|
212385
|
+
projectKindHints: ["backend"],
|
|
212386
|
+
apiStyleHint: hasRoutesApi || hasLaravelRouteFacade ? "rest" : "unknown"
|
|
212387
|
+
});
|
|
212388
|
+
},
|
|
212389
|
+
resolveProjectKinds: () => ["backend"],
|
|
212390
|
+
getImportExtractionMode: () => "php-imports"
|
|
212391
|
+
}),
|
|
212194
212392
|
php: buildAdapter({
|
|
212195
212393
|
id: "php",
|
|
212196
212394
|
displayName: canonicalStackLabels.php,
|
|
@@ -212478,23 +212676,182 @@ var stackAdaptersById = {
|
|
|
212478
212676
|
displayName: canonicalStackLabels.ruby_rails,
|
|
212479
212677
|
ecosystem: "other",
|
|
212480
212678
|
category: "framework",
|
|
212481
|
-
priority:
|
|
212482
|
-
manifestDetectionHints: ["Gemfile"],
|
|
212679
|
+
priority: 70,
|
|
212680
|
+
manifestDetectionHints: ["Gemfile", "rails", "routes.rb", "application.rb"],
|
|
212483
212681
|
buildToolHints: ["bundle"],
|
|
212484
212682
|
defaults: {
|
|
212485
|
-
sourceRoots: ["app"],
|
|
212486
|
-
moduleRootCandidates: ["app"],
|
|
212487
|
-
apiRoots: ["app/controllers"],
|
|
212683
|
+
sourceRoots: ["app", "config"],
|
|
212684
|
+
moduleRootCandidates: ["app/services", "app/controllers", "app/models", "app"],
|
|
212685
|
+
apiRoots: ["app/controllers", "config/routes.rb"],
|
|
212488
212686
|
databaseRoots: ["db"],
|
|
212489
|
-
configFiles: ["Gemfile"],
|
|
212687
|
+
configFiles: ["Gemfile", "config/routes.rb", "config/application.rb"],
|
|
212490
212688
|
fileExtensions: [".rb"],
|
|
212491
212689
|
dependencyFileExtensions: [".rb"],
|
|
212492
|
-
ignoreDirectories: [...commonIgnoreDirectories],
|
|
212690
|
+
ignoreDirectories: ["vendor", "tmp", "log", ...commonIgnoreDirectories],
|
|
212493
212691
|
entrypointHints: ["config/application.rb"],
|
|
212494
212692
|
publicEntrypointDirectories: [],
|
|
212495
212693
|
moduleIndexFileName: "application.rb"
|
|
212496
212694
|
},
|
|
212497
|
-
detect: () =>
|
|
212695
|
+
detect: (context) => {
|
|
212696
|
+
const evidence = [];
|
|
212697
|
+
let strongSignals = 0;
|
|
212698
|
+
let mediumSignals = 0;
|
|
212699
|
+
let score = 0;
|
|
212700
|
+
const hasRailsGem = context.hasFileMatchingPattern("Gemfile", /\bgem\s+['"]rails['"]/u);
|
|
212701
|
+
const hasBinRails = context.hasAnyPathMatchingPattern(/(^|\/)bin\/rails$/u);
|
|
212702
|
+
const hasRoutes = context.hasAnyPathMatchingPattern(/(^|\/)config\/routes\.rb$/u);
|
|
212703
|
+
const hasControllers = context.hasDirectory("app/controllers");
|
|
212704
|
+
const hasModels = context.hasDirectory("app/models");
|
|
212705
|
+
const hasServices = context.hasDirectory("app/services");
|
|
212706
|
+
const hasRailsRoutesDraw = context.hasFileMatchingPattern("routes.rb", /\bRails\.application\.routes\.draw\b/u);
|
|
212707
|
+
const hasApplicationClass = context.hasAnyFileMatchingPattern(
|
|
212708
|
+
/(^|\/)config\/application\.rb$/u,
|
|
212709
|
+
/\bclass\s+Application\s+<\s+Rails::Application\b/u
|
|
212710
|
+
);
|
|
212711
|
+
const hasApplicationController = context.hasAnyFileMatchingPattern(
|
|
212712
|
+
/(^|\/)app\/controllers\/.+\.rb$/u,
|
|
212713
|
+
/\bApplicationController\b/u
|
|
212714
|
+
);
|
|
212715
|
+
if (hasRailsGem) {
|
|
212716
|
+
strongSignals += 1;
|
|
212717
|
+
score += 48;
|
|
212718
|
+
evidence.push("Found Gemfile with rails gem");
|
|
212719
|
+
}
|
|
212720
|
+
if (hasBinRails) {
|
|
212721
|
+
strongSignals += 1;
|
|
212722
|
+
score += 18;
|
|
212723
|
+
evidence.push("Found bin/rails");
|
|
212724
|
+
}
|
|
212725
|
+
if (hasRoutes) {
|
|
212726
|
+
mediumSignals += 1;
|
|
212727
|
+
score += 10;
|
|
212728
|
+
evidence.push("Found config/routes.rb");
|
|
212729
|
+
}
|
|
212730
|
+
if (hasControllers) {
|
|
212731
|
+
mediumSignals += 1;
|
|
212732
|
+
score += 8;
|
|
212733
|
+
evidence.push("Found app/controllers directory");
|
|
212734
|
+
}
|
|
212735
|
+
if (hasModels) {
|
|
212736
|
+
mediumSignals += 1;
|
|
212737
|
+
score += 6;
|
|
212738
|
+
evidence.push("Found app/models directory");
|
|
212739
|
+
}
|
|
212740
|
+
if (hasServices) {
|
|
212741
|
+
mediumSignals += 1;
|
|
212742
|
+
score += 5;
|
|
212743
|
+
evidence.push("Found app/services directory");
|
|
212744
|
+
}
|
|
212745
|
+
if (hasRailsRoutesDraw) {
|
|
212746
|
+
mediumSignals += 1;
|
|
212747
|
+
score += 8;
|
|
212748
|
+
evidence.push("Found Rails routes draw block");
|
|
212749
|
+
}
|
|
212750
|
+
if (hasApplicationClass) {
|
|
212751
|
+
mediumSignals += 1;
|
|
212752
|
+
score += 8;
|
|
212753
|
+
evidence.push("Found Rails application class");
|
|
212754
|
+
}
|
|
212755
|
+
if (hasApplicationController) {
|
|
212756
|
+
mediumSignals += 1;
|
|
212757
|
+
score += 5;
|
|
212758
|
+
evidence.push("Found ApplicationController inheritance");
|
|
212759
|
+
}
|
|
212760
|
+
if (!hasRailsGem || !(hasBinRails || hasRoutes || hasControllers || hasApplicationClass)) {
|
|
212761
|
+
return null;
|
|
212762
|
+
}
|
|
212763
|
+
return toResult({
|
|
212764
|
+
adapterId: "ruby_rails",
|
|
212765
|
+
confidence: strongSignals >= 2 || mediumSignals >= 3 ? "high" : "medium",
|
|
212766
|
+
score,
|
|
212767
|
+
evidence,
|
|
212768
|
+
projectKindHints: ["backend"],
|
|
212769
|
+
apiStyleHint: hasRoutes || hasControllers ? "rest" : "unknown"
|
|
212770
|
+
});
|
|
212771
|
+
},
|
|
212772
|
+
resolveProjectKinds: () => ["backend"],
|
|
212773
|
+
getImportExtractionMode: () => "generic"
|
|
212774
|
+
}),
|
|
212775
|
+
kotlin: buildAdapter({
|
|
212776
|
+
id: "kotlin",
|
|
212777
|
+
displayName: canonicalStackLabels.kotlin,
|
|
212778
|
+
ecosystem: "jvm",
|
|
212779
|
+
category: "generic-language",
|
|
212780
|
+
priority: 40,
|
|
212781
|
+
manifestDetectionHints: ["build.gradle.kts", "pom.xml", "settings.gradle.kts"],
|
|
212782
|
+
buildToolHints: ["gradle", "maven"],
|
|
212783
|
+
defaults: {
|
|
212784
|
+
sourceRoots: ["src/main/kotlin", "src"],
|
|
212785
|
+
moduleRootCandidates: ["src/main/kotlin", "src"],
|
|
212786
|
+
apiRoots: ["src/main/kotlin"],
|
|
212787
|
+
databaseRoots: ["src/main/resources/db", "src/main/resources/migrations"],
|
|
212788
|
+
configFiles: ["build.gradle.kts", "pom.xml", "settings.gradle.kts"],
|
|
212789
|
+
fileExtensions: [".kt", ".kts"],
|
|
212790
|
+
dependencyFileExtensions: [".kt", ".kts"],
|
|
212791
|
+
ignoreDirectories: [
|
|
212792
|
+
"target",
|
|
212793
|
+
"build",
|
|
212794
|
+
".gradle",
|
|
212795
|
+
".idea",
|
|
212796
|
+
"out",
|
|
212797
|
+
...commonIgnoreDirectories
|
|
212798
|
+
],
|
|
212799
|
+
entrypointHints: ["Application.kt", "Main.kt"],
|
|
212800
|
+
publicEntrypointDirectories: [],
|
|
212801
|
+
moduleIndexFileName: "Application.kt"
|
|
212802
|
+
},
|
|
212803
|
+
detect: (context) => {
|
|
212804
|
+
const evidence = [];
|
|
212805
|
+
let score = 0;
|
|
212806
|
+
const hasBuildGradleKts = context.hasFile("build.gradle.kts");
|
|
212807
|
+
const hasSettingsGradleKts = context.hasFile("settings.gradle.kts");
|
|
212808
|
+
const hasKotlinPluginInGradle = context.hasFileMatchingPattern("build.gradle.kts", /\bkotlin\s*\(\s*["']jvm["']\s*\)/u) || context.hasFileMatchingPattern("build.gradle.kts", /\bid\s*\(\s*["']org\.jetbrains\.kotlin\.jvm["']\s*\)/u);
|
|
212809
|
+
const hasPomKotlinPlugin = context.hasFileMatchingPattern("pom.xml", /\bkotlin-maven-plugin\b/u) || context.hasFileMatchingPattern("pom.xml", /\borg\.jetbrains\.kotlin\b/u);
|
|
212810
|
+
const hasKotlinSourceRoot = context.hasDirectory("src/main/kotlin");
|
|
212811
|
+
const hasKtFiles = context.hasFileExtension(".kt") || context.hasFileExtension(".kts");
|
|
212812
|
+
const hasKotlinPackageShape = context.hasDirectory("src/main/kotlin/com") || context.hasDirectory("src/main/kotlin/org");
|
|
212813
|
+
if (hasBuildGradleKts) {
|
|
212814
|
+
score += 30;
|
|
212815
|
+
evidence.push("Found build.gradle.kts");
|
|
212816
|
+
}
|
|
212817
|
+
if (hasSettingsGradleKts) {
|
|
212818
|
+
score += 8;
|
|
212819
|
+
evidence.push("Found settings.gradle.kts");
|
|
212820
|
+
}
|
|
212821
|
+
if (hasKotlinPluginInGradle) {
|
|
212822
|
+
score += 32;
|
|
212823
|
+
evidence.push("Found Kotlin JVM plugin in build.gradle.kts");
|
|
212824
|
+
}
|
|
212825
|
+
if (hasPomKotlinPlugin) {
|
|
212826
|
+
score += 32;
|
|
212827
|
+
evidence.push("Found Kotlin Maven plugin markers in pom.xml");
|
|
212828
|
+
}
|
|
212829
|
+
if (hasKotlinSourceRoot) {
|
|
212830
|
+
score += 18;
|
|
212831
|
+
evidence.push("Found src/main/kotlin source root");
|
|
212832
|
+
}
|
|
212833
|
+
if (hasKtFiles) {
|
|
212834
|
+
score += 16;
|
|
212835
|
+
evidence.push("Found Kotlin source files");
|
|
212836
|
+
}
|
|
212837
|
+
if (hasKotlinPackageShape) {
|
|
212838
|
+
score += 4;
|
|
212839
|
+
evidence.push("Found conventional Kotlin package directory under src/main/kotlin");
|
|
212840
|
+
}
|
|
212841
|
+
const hasSafeKotlinEvidence = hasKtFiles && (hasBuildGradleKts || hasKotlinPluginInGradle || hasPomKotlinPlugin || hasKotlinSourceRoot);
|
|
212842
|
+
if (!hasSafeKotlinEvidence) {
|
|
212843
|
+
return null;
|
|
212844
|
+
}
|
|
212845
|
+
return toResult({
|
|
212846
|
+
adapterId: "kotlin",
|
|
212847
|
+
confidence: (hasKotlinPluginInGradle || hasPomKotlinPlugin) && hasKotlinSourceRoot ? "high" : hasBuildGradleKts || hasKotlinSourceRoot ? "medium" : "low",
|
|
212848
|
+
score,
|
|
212849
|
+
evidence,
|
|
212850
|
+
projectKindHints: ["backend", "library"],
|
|
212851
|
+
apiStyleHint: "unknown"
|
|
212852
|
+
});
|
|
212853
|
+
},
|
|
212854
|
+
resolveProjectKinds: () => ["backend", "library"],
|
|
212498
212855
|
getImportExtractionMode: () => "generic"
|
|
212499
212856
|
}),
|
|
212500
212857
|
kotlin_spring: buildAdapter({
|
|
@@ -212750,7 +213107,7 @@ var stackAdaptersById = {
|
|
|
212750
213107
|
let score = 0;
|
|
212751
213108
|
if (hasVueDependency) {
|
|
212752
213109
|
strongSignals += 1;
|
|
212753
|
-
score +=
|
|
213110
|
+
score += 30;
|
|
212754
213111
|
evidence.push("Found package.json with vue dependency");
|
|
212755
213112
|
}
|
|
212756
213113
|
if (hasAppVue) {
|
|
@@ -212765,12 +213122,12 @@ var stackAdaptersById = {
|
|
|
212765
213122
|
}
|
|
212766
213123
|
if (hasVitePluginInConfig) {
|
|
212767
213124
|
strongSignals += 1;
|
|
212768
|
-
score +=
|
|
213125
|
+
score += 24;
|
|
212769
213126
|
evidence.push("Found Vue plugin usage in vite.config.ts or vite.config.js");
|
|
212770
213127
|
}
|
|
212771
213128
|
if (hasVitePluginDependency) {
|
|
212772
213129
|
strongSignals += 1;
|
|
212773
|
-
score +=
|
|
213130
|
+
score += 22;
|
|
212774
213131
|
evidence.push("Found package.json with @vitejs/plugin-vue dependency");
|
|
212775
213132
|
}
|
|
212776
213133
|
if (hasSrcViews) {
|
|
@@ -212785,12 +213142,12 @@ var stackAdaptersById = {
|
|
|
212785
213142
|
}
|
|
212786
213143
|
if (hasCreateAppReference) {
|
|
212787
213144
|
mediumSignals += 1;
|
|
212788
|
-
score +=
|
|
213145
|
+
score += 12;
|
|
212789
213146
|
evidence.push("Found createApp usage in src/main.ts or src/main.js");
|
|
212790
213147
|
}
|
|
212791
213148
|
if (hasViteConfig && hasVueDependency) {
|
|
212792
213149
|
mediumSignals += 1;
|
|
212793
|
-
score +=
|
|
213150
|
+
score += 8;
|
|
212794
213151
|
evidence.push("Found vite.config.ts or vite.config.js with Vue package evidence");
|
|
212795
213152
|
}
|
|
212796
213153
|
if (hasTsConfigApp) {
|
|
@@ -213118,7 +213475,264 @@ async function listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath =
|
|
|
213118
213475
|
const entries = await import_node_fs.promises.readdir(modulesRoot, { withFileTypes: true });
|
|
213119
213476
|
return entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
|
|
213120
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
|
+
}
|
|
213121
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
|
+
}
|
|
213122
213736
|
const moduleDirectories = await listModuleDirectoryNames(workspaceRoot, modulesRootRelativePath);
|
|
213123
213737
|
const modulesRoot = path.join(workspaceRoot, ...modulesRootRelativePath.split("/"));
|
|
213124
213738
|
return new Map(
|
|
@@ -214497,7 +215111,8 @@ function buildArchitectureContract(base, overrides) {
|
|
|
214497
215111
|
}
|
|
214498
215112
|
function buildDefaultModuleContractPath(moduleName, contract) {
|
|
214499
215113
|
const contractsRoot = resolveContractsRootFromContract(contract);
|
|
214500
|
-
|
|
215114
|
+
const safeModuleName = moduleName.replace(/\\/g, "/").split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
215115
|
+
return `${contractsRoot}/${safeModuleName}.contract.json`;
|
|
214501
215116
|
}
|
|
214502
215117
|
async function loadArchitectureContract(workspaceRoot) {
|
|
214503
215118
|
const configPath = path7.join(workspaceRoot, ".archpilot", "architecture.json");
|
|
@@ -214628,8 +215243,63 @@ var DependencyRulesConfigError = class extends Error {
|
|
|
214628
215243
|
function normalizePath2(filePath) {
|
|
214629
215244
|
return filePath.replace(/\\/g, "/");
|
|
214630
215245
|
}
|
|
214631
|
-
function
|
|
214632
|
-
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
|
+
);
|
|
214633
215303
|
}
|
|
214634
215304
|
function getScriptKind(filePath) {
|
|
214635
215305
|
const extension = path8.extname(filePath).toLowerCase();
|
|
@@ -214670,6 +215340,37 @@ async function readTextFileIfExists2(filePath) {
|
|
|
214670
215340
|
return void 0;
|
|
214671
215341
|
}
|
|
214672
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
|
+
}
|
|
214673
215374
|
function validateDependencyModuleRules(moduleName, value) {
|
|
214674
215375
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
214675
215376
|
throw new DependencyRulesConfigError(
|
|
@@ -215143,7 +215844,32 @@ async function resolveImportPath(basePath, adapter) {
|
|
|
215143
215844
|
}
|
|
215144
215845
|
return void 0;
|
|
215145
215846
|
}
|
|
215146
|
-
|
|
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) {
|
|
215147
215873
|
let basePath;
|
|
215148
215874
|
const normalizedModulesRoot = normalizePath2(modulesRootRelativePath).replace(/^\/+/, "");
|
|
215149
215875
|
if (usesJavaImportParser(adapter) && !importSpecifier.startsWith(".")) {
|
|
@@ -215198,6 +215924,22 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215198
215924
|
basePath = path8.join(workspaceRoot, importSpecifier.slice(1));
|
|
215199
215925
|
} else if (normalizedModulesRoot.startsWith("src/") && importSpecifier.startsWith(`${normalizedModulesRoot.slice("src/".length)}/`)) {
|
|
215200
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
|
+
}
|
|
215201
215943
|
}
|
|
215202
215944
|
if (!basePath) {
|
|
215203
215945
|
return void 0;
|
|
@@ -215206,17 +215948,8 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215206
215948
|
if (!resolvedPath) {
|
|
215207
215949
|
return void 0;
|
|
215208
215950
|
}
|
|
215209
|
-
const
|
|
215210
|
-
|
|
215211
|
-
if (relativeToModulesRoot === "" || relativeToModulesRoot.startsWith("../") || relativeToModulesRoot.startsWith("..\\")) {
|
|
215212
|
-
return void 0;
|
|
215213
|
-
}
|
|
215214
|
-
const segments = relativeToModulesRoot.split("/");
|
|
215215
|
-
if (segments.length === 0) {
|
|
215216
|
-
return void 0;
|
|
215217
|
-
}
|
|
215218
|
-
const targetModule = segments[0];
|
|
215219
|
-
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) {
|
|
215220
215953
|
const standaloneTargetModule = explicitStandaloneModuleByPath.get(
|
|
215221
215954
|
normalizePath2(path8.relative(workspaceRoot, resolvedPath))
|
|
215222
215955
|
);
|
|
@@ -215229,7 +215962,11 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215229
215962
|
isPublicImport: true
|
|
215230
215963
|
};
|
|
215231
215964
|
}
|
|
215232
|
-
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
|
+
}
|
|
215233
215970
|
const indexEntrypointCandidates = adapter.dependencyParsingFileExtensions.map(
|
|
215234
215971
|
(extension) => `index${extension}`
|
|
215235
215972
|
);
|
|
@@ -215242,7 +215979,7 @@ async function resolveCrossModuleImport(workspaceRoot, modulesRootRelativePath,
|
|
|
215242
215979
|
isPublicImport
|
|
215243
215980
|
};
|
|
215244
215981
|
}
|
|
215245
|
-
async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, adapter, options) {
|
|
215982
|
+
async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath, moduleRoots, explicitStandaloneModuleByPath, workspacePackageAliases, adapter, options) {
|
|
215246
215983
|
const imports = [];
|
|
215247
215984
|
const includeTestFiles = options?.includeTestFiles === true;
|
|
215248
215985
|
if (!supportsDependencyImportParsing(adapter)) {
|
|
@@ -215251,7 +215988,10 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
|
|
|
215251
215988
|
for (const [moduleName, moduleRoot] of [...moduleRoots.entries()].sort(
|
|
215252
215989
|
([left], [right]) => left.localeCompare(right)
|
|
215253
215990
|
)) {
|
|
215254
|
-
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
|
+
);
|
|
215255
215995
|
for (const sourceFilePath of sourceFiles) {
|
|
215256
215996
|
const sourceFileRelativePath = normalizePath2(
|
|
215257
215997
|
path8.relative(workspaceRoot, sourceFilePath)
|
|
@@ -215274,6 +216014,7 @@ async function collectCrossModuleImports(workspaceRoot, modulesRootRelativePath,
|
|
|
215274
216014
|
modulesRootRelativePath,
|
|
215275
216015
|
moduleRoots,
|
|
215276
216016
|
explicitStandaloneModuleByPath,
|
|
216017
|
+
workspacePackageAliases,
|
|
215277
216018
|
sourceFilePath,
|
|
215278
216019
|
importReference.specifier,
|
|
215279
216020
|
adapter
|
|
@@ -215419,7 +216160,7 @@ async function loadModuleArchitectureContract(workspaceRoot, architectureContrac
|
|
|
215419
216160
|
const registryEntry = architectureContract.modules[moduleName];
|
|
215420
216161
|
const contractPath = registryEntry?.contract ?? path8.posix.join(
|
|
215421
216162
|
resolveContractsRootFromContract(architectureContract),
|
|
215422
|
-
`${moduleName}.contract.json`
|
|
216163
|
+
`${toSafeContractModuleFileStem(moduleName)}.contract.json`
|
|
215423
216164
|
);
|
|
215424
216165
|
const fullContractPath = path8.join(workspaceRoot, ...contractPath.split("/"));
|
|
215425
216166
|
const content = await readTextFileIfExists2(fullContractPath);
|
|
@@ -215566,6 +216307,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215566
216307
|
workspaceRoot,
|
|
215567
216308
|
architectureContract
|
|
215568
216309
|
);
|
|
216310
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
215569
216311
|
const moduleIds = sortUnique2([...moduleRoots.keys(), ...explicitStandaloneModuleByPath.values()]);
|
|
215570
216312
|
const registeredModuleIds = new Set(moduleIds);
|
|
215571
216313
|
const imports = await collectCrossModuleImports(
|
|
@@ -215573,9 +216315,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215573
216315
|
modulesRoot,
|
|
215574
216316
|
moduleRoots,
|
|
215575
216317
|
explicitStandaloneModuleByPath,
|
|
216318
|
+
workspacePackageAliases,
|
|
215576
216319
|
adapter
|
|
215577
216320
|
);
|
|
215578
|
-
const
|
|
216321
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
216322
|
+
const actualBySource = buildActualImportedModuleSetBySource(peerImports, registeredModuleIds);
|
|
215579
216323
|
const moduleContractCache = /* @__PURE__ */ new Map();
|
|
215580
216324
|
const declaredBySource = /* @__PURE__ */ new Map();
|
|
215581
216325
|
const hasContractByModule = /* @__PURE__ */ new Map();
|
|
@@ -215593,7 +216337,7 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215593
216337
|
new Set(
|
|
215594
216338
|
sortUnique2(
|
|
215595
216339
|
sourceContract.contract.dependsOn.filter(
|
|
215596
|
-
(dependencyModule) => registeredModuleIds.has(dependencyModule)
|
|
216340
|
+
(dependencyModule) => registeredModuleIds.has(dependencyModule) && !isParentChildModuleRelationship(moduleId, dependencyModule)
|
|
215597
216341
|
)
|
|
215598
216342
|
)
|
|
215599
216343
|
)
|
|
@@ -215605,6 +216349,11 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215605
216349
|
}
|
|
215606
216350
|
const inboundDeclared = buildInboundDependencyMap(declaredBySource, moduleIds);
|
|
215607
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
|
+
);
|
|
215608
216357
|
return {
|
|
215609
216358
|
modules: moduleIds.map((moduleId) => {
|
|
215610
216359
|
const declaredDependencies = sortUnique2(declaredBySource.get(moduleId) ?? []);
|
|
@@ -215618,6 +216367,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215618
216367
|
return {
|
|
215619
216368
|
module: moduleId,
|
|
215620
216369
|
hasContract: hasContractByModule.get(moduleId) ?? false,
|
|
216370
|
+
...parentModuleByChild.has(moduleId) ? { parentModule: parentModuleByChild.get(moduleId) } : {},
|
|
216371
|
+
childModules: childModulesByParent.get(moduleId) ?? [],
|
|
215621
216372
|
declaredDependencies,
|
|
215622
216373
|
actualDependencies,
|
|
215623
216374
|
inboundDeclaredFrom: inboundDeclared.get(moduleId) ?? [],
|
|
@@ -215625,7 +216376,8 @@ async function buildDependencyGraphSummary(workspaceRoot, architectureContract,
|
|
|
215625
216376
|
unusedDeclaredDependencies,
|
|
215626
216377
|
undeclaredActualDependencies
|
|
215627
216378
|
};
|
|
215628
|
-
})
|
|
216379
|
+
}),
|
|
216380
|
+
hierarchyEdges
|
|
215629
216381
|
};
|
|
215630
216382
|
}
|
|
215631
216383
|
async function validateDependencyContractBoundaries(workspaceRoot, architectureContract, options) {
|
|
@@ -215639,19 +216391,22 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215639
216391
|
workspaceRoot,
|
|
215640
216392
|
architectureContract
|
|
215641
216393
|
);
|
|
216394
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
215642
216395
|
const imports = await collectCrossModuleImports(
|
|
215643
216396
|
workspaceRoot,
|
|
215644
216397
|
modulesRoot,
|
|
215645
216398
|
moduleRoots,
|
|
215646
216399
|
explicitStandaloneModuleByPath,
|
|
216400
|
+
workspacePackageAliases,
|
|
215647
216401
|
adapter
|
|
215648
216402
|
);
|
|
216403
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
215649
216404
|
const findings = [];
|
|
215650
216405
|
const moduleContractCache = /* @__PURE__ */ new Map();
|
|
215651
216406
|
if (options.checkDeclaredDependencies) {
|
|
215652
216407
|
const failures = [];
|
|
215653
216408
|
const skippedFindings = /* @__PURE__ */ new Map();
|
|
215654
|
-
for (const dependencyImport of
|
|
216409
|
+
for (const dependencyImport of peerImports) {
|
|
215655
216410
|
const sourceContract = await loadModuleArchitectureContract(
|
|
215656
216411
|
workspaceRoot,
|
|
215657
216412
|
architectureContract,
|
|
@@ -215723,7 +216478,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215723
216478
|
}
|
|
215724
216479
|
if (failures.length > 0) {
|
|
215725
216480
|
findings.push(...failures);
|
|
215726
|
-
} else if (
|
|
216481
|
+
} else if (peerImports.length === 0) {
|
|
215727
216482
|
findings.push({
|
|
215728
216483
|
result: makeValidationResult(
|
|
215729
216484
|
"AP-DEP-004",
|
|
@@ -215748,7 +216503,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215748
216503
|
if (options.checkPublicSurfaceImports) {
|
|
215749
216504
|
const failures = [];
|
|
215750
216505
|
const skippedFindings = /* @__PURE__ */ new Map();
|
|
215751
|
-
for (const dependencyImport of
|
|
216506
|
+
for (const dependencyImport of peerImports) {
|
|
215752
216507
|
const sourceContract = await loadModuleArchitectureContract(
|
|
215753
216508
|
workspaceRoot,
|
|
215754
216509
|
architectureContract,
|
|
@@ -215815,17 +216570,18 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215815
216570
|
}
|
|
215816
216571
|
continue;
|
|
215817
216572
|
}
|
|
215818
|
-
if (targetContract.contract.publicEntrypoints.length > 0 &&
|
|
215819
|
-
|
|
215820
|
-
|
|
215821
|
-
adapter
|
|
216573
|
+
if (targetContract.contract.publicEntrypoints.length > 0 && !isImportWithinPublicEntrypoints(
|
|
216574
|
+
dependencyImport.resolvedTargetRelativePath,
|
|
216575
|
+
targetContract.contract.publicEntrypoints
|
|
215822
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";
|
|
215823
216579
|
failures.push({
|
|
215824
216580
|
result: makeValidationResult(
|
|
215825
216581
|
"AP-DEP-005",
|
|
215826
216582
|
"error",
|
|
215827
216583
|
false,
|
|
215828
|
-
`Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its
|
|
216584
|
+
`Module '${dependencyImport.sourceModule}' must import module '${dependencyImport.targetModule}' via its ${publicEntrypointLabel}: ${expectedEntrypoint}.`,
|
|
215829
216585
|
{
|
|
215830
216586
|
findingType: "module-dependency",
|
|
215831
216587
|
module: dependencyImport.sourceModule,
|
|
@@ -215866,7 +216622,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215866
216622
|
}
|
|
215867
216623
|
if (failures.length > 0) {
|
|
215868
216624
|
findings.push(...failures);
|
|
215869
|
-
} else if (
|
|
216625
|
+
} else if (peerImports.length === 0) {
|
|
215870
216626
|
findings.push({
|
|
215871
216627
|
result: makeValidationResult(
|
|
215872
216628
|
"AP-DEP-005",
|
|
@@ -215895,7 +216651,7 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215895
216651
|
...explicitStandaloneModuleByPath.values()
|
|
215896
216652
|
]);
|
|
215897
216653
|
const importedBySource = buildActualImportedModuleSetBySource(
|
|
215898
|
-
|
|
216654
|
+
peerImports,
|
|
215899
216655
|
registeredModuleIds
|
|
215900
216656
|
);
|
|
215901
216657
|
for (const moduleName of [...registeredModuleIds].sort(
|
|
@@ -215910,7 +216666,9 @@ async function validateDependencyContractBoundaries(workspaceRoot, architectureC
|
|
|
215910
216666
|
if (sourceContract.status !== "ok" || !sourceContract.contract) {
|
|
215911
216667
|
continue;
|
|
215912
216668
|
}
|
|
215913
|
-
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));
|
|
215914
216672
|
if (declaredDependencies.length === 0) {
|
|
215915
216673
|
continue;
|
|
215916
216674
|
}
|
|
@@ -215965,13 +216723,16 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
215965
216723
|
workspaceRoot,
|
|
215966
216724
|
parsedArchitectureContract
|
|
215967
216725
|
);
|
|
216726
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
215968
216727
|
const imports = await collectCrossModuleImports(
|
|
215969
216728
|
workspaceRoot,
|
|
215970
216729
|
modulesRootRelativePath,
|
|
215971
216730
|
moduleRoots,
|
|
215972
216731
|
explicitStandaloneModuleByPath,
|
|
216732
|
+
workspacePackageAliases,
|
|
215973
216733
|
adapter
|
|
215974
216734
|
);
|
|
216735
|
+
const peerImports = filterPeerModuleImports(imports);
|
|
215975
216736
|
const findings = [];
|
|
215976
216737
|
let dependencyRules;
|
|
215977
216738
|
let dependencyRulesSkipReason;
|
|
@@ -215992,7 +216753,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
215992
216753
|
}
|
|
215993
216754
|
}
|
|
215994
216755
|
if (options.checkCircularDependencies) {
|
|
215995
|
-
const cycles = detectCircularDependencies([...moduleRoots.keys()],
|
|
216756
|
+
const cycles = detectCircularDependencies([...moduleRoots.keys()], peerImports);
|
|
215996
216757
|
if (cycles.length === 0) {
|
|
215997
216758
|
findings.push({
|
|
215998
216759
|
result: makeValidationResult(
|
|
@@ -216024,7 +216785,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216024
216785
|
})
|
|
216025
216786
|
);
|
|
216026
216787
|
} else {
|
|
216027
|
-
const violations =
|
|
216788
|
+
const violations = peerImports.filter((dependencyImport) => {
|
|
216028
216789
|
const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
|
|
216029
216790
|
if (!moduleRules) {
|
|
216030
216791
|
return false;
|
|
@@ -216076,7 +216837,7 @@ async function validateDependencyBoundaries(workspaceRoot, options) {
|
|
|
216076
216837
|
})
|
|
216077
216838
|
);
|
|
216078
216839
|
} else {
|
|
216079
|
-
const violations =
|
|
216840
|
+
const violations = peerImports.filter((dependencyImport) => {
|
|
216080
216841
|
const moduleRules = dependencyRules.modules[dependencyImport.sourceModule];
|
|
216081
216842
|
return moduleRules?.publicEntrypointsOnly === true && !dependencyImport.isPublicImport;
|
|
216082
216843
|
});
|
|
@@ -216124,6 +216885,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216124
216885
|
workspaceRoot,
|
|
216125
216886
|
parsedArchitectureContract
|
|
216126
216887
|
);
|
|
216888
|
+
const workspacePackageAliases = await buildWorkspacePackageAliasMap(moduleRoots);
|
|
216127
216889
|
const discoveredModules = sortUnique2([
|
|
216128
216890
|
...moduleRoots.keys(),
|
|
216129
216891
|
...explicitStandaloneModuleByPath.values()
|
|
@@ -216136,9 +216898,10 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216136
216898
|
modulesRootRelativePath,
|
|
216137
216899
|
moduleRoots,
|
|
216138
216900
|
explicitStandaloneModuleByPath,
|
|
216901
|
+
workspacePackageAliases,
|
|
216139
216902
|
adapter
|
|
216140
216903
|
);
|
|
216141
|
-
const imports = allImports.filter(
|
|
216904
|
+
const imports = filterPeerModuleImports(allImports).filter(
|
|
216142
216905
|
(dependencyImport) => moduleSet.has(dependencyImport.sourceModule) && moduleSet.has(dependencyImport.targetModule)
|
|
216143
216906
|
).sort((left, right) => {
|
|
216144
216907
|
const sourceCompare = left.sourceModule.localeCompare(right.sourceModule);
|
|
@@ -216191,6 +216954,7 @@ async function buildModuleDependencyGraphSnapshot(workspaceRoot, options) {
|
|
|
216191
216954
|
return {
|
|
216192
216955
|
modules,
|
|
216193
216956
|
edges,
|
|
216957
|
+
hierarchyEdges: buildModuleHierarchyEdges(modules),
|
|
216194
216958
|
cycles,
|
|
216195
216959
|
imports
|
|
216196
216960
|
};
|
|
@@ -219592,11 +220356,11 @@ src/
|
|
|
219592
220356
|
},
|
|
219593
220357
|
DEP_ISOLATED_MODULE_DETECTED: {
|
|
219594
220358
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
219595
|
-
title: "
|
|
220359
|
+
title: "Potential orphan module detected",
|
|
219596
220360
|
category: "dependency",
|
|
219597
220361
|
defaultSeverity: "warning",
|
|
219598
|
-
description: "Checks whether a registered module has no
|
|
219599
|
-
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."
|
|
219600
220364
|
},
|
|
219601
220365
|
DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY: {
|
|
219602
220366
|
id: ValidationRuleIds.DEP_TRANSITIVE_CIRCULAR_MODULE_DEPENDENCY,
|
|
@@ -220815,8 +221579,17 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
|
|
|
220815
221579
|
const modulesRootRelativePath2 = await getConfiguredModulesRoot(workspaceRoot, contract);
|
|
220816
221580
|
const modulesRoot = path21.join(workspaceRoot, ...modulesRootRelativePath2.split("/"));
|
|
220817
221581
|
if (await pathExists7(modulesRoot)) {
|
|
220818
|
-
const
|
|
220819
|
-
|
|
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));
|
|
220820
221593
|
const dependencySummary = await buildDependencyGraphSummary(workspaceRoot, contract, {
|
|
220821
221594
|
modulesRootRelativePath: modulesRootRelativePath2
|
|
220822
221595
|
});
|
|
@@ -220824,8 +221597,9 @@ async function validateArchitectureStyle(workspaceRoot, contract, validationConf
|
|
|
220824
221597
|
dependencySummary.modules.filter((moduleSummary) => moduleSummary.inboundDeclaredFrom.length > 0).map((moduleSummary) => moduleSummary.module)
|
|
220825
221598
|
);
|
|
220826
221599
|
const incompleteModules = [];
|
|
220827
|
-
for (const
|
|
220828
|
-
const
|
|
221600
|
+
for (const moduleDirectory of moduleDirectories2) {
|
|
221601
|
+
const moduleName = moduleDirectory.moduleName;
|
|
221602
|
+
const moduleDir = path21.join(workspaceRoot, ...moduleDirectory.sourcePath.split("/"));
|
|
220829
221603
|
const readmeExists = await pathExists7(path21.join(moduleDir, "README.md"));
|
|
220830
221604
|
const indexExists = await pathExists7(path21.join(moduleDir, "index.ts"));
|
|
220831
221605
|
const registryPublicEntrypoints = (contract.modules[moduleName]?.publicEntrypoints ?? []).filter((entry) => typeof entry === "string").map((entry) => normalizeEntrypoint(entry));
|
|
@@ -220954,18 +221728,111 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
220954
221728
|
return [];
|
|
220955
221729
|
}
|
|
220956
221730
|
const isolatedResults = [];
|
|
220957
|
-
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
|
+
]);
|
|
220958
221751
|
const isTestLikeModule = (moduleName) => {
|
|
220959
221752
|
const normalized = moduleName.toLowerCase();
|
|
220960
221753
|
return normalized === "test" || normalized === "tests" || normalized.endsWith("-test") || normalized.endsWith("-tests");
|
|
220961
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
|
+
};
|
|
220962
221832
|
for (const moduleSummary of dependencyGraphSummary.modules) {
|
|
220963
221833
|
if (isTestLikeModule(moduleSummary.module)) {
|
|
220964
221834
|
continue;
|
|
220965
221835
|
}
|
|
220966
|
-
if (isolationExemptInfrastructureModules.has(moduleSummary.module.toLowerCase())) {
|
|
220967
|
-
continue;
|
|
220968
|
-
}
|
|
220969
221836
|
const registryEntry = contract.modules[moduleSummary.module];
|
|
220970
221837
|
if (!registryEntry?.path) {
|
|
220971
221838
|
continue;
|
|
@@ -220979,11 +221846,14 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
220979
221846
|
if (!isIsolated) {
|
|
220980
221847
|
continue;
|
|
220981
221848
|
}
|
|
221849
|
+
if (await hasIntentionalIndependentEvidence2(moduleSummary, moduleDirectoryPath)) {
|
|
221850
|
+
continue;
|
|
221851
|
+
}
|
|
220982
221852
|
isolatedResults.push({
|
|
220983
221853
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
220984
221854
|
severity: "warning",
|
|
220985
221855
|
passed: false,
|
|
220986
|
-
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.`,
|
|
220987
221857
|
findingType: "module-dependency",
|
|
220988
221858
|
module: moduleSummary.module
|
|
220989
221859
|
});
|
|
@@ -220996,7 +221866,7 @@ async function validateDependencyGraphIsolation(workspaceRoot, contract, depende
|
|
|
220996
221866
|
id: ValidationRuleIds.DEP_ISOLATED_MODULE_DETECTED,
|
|
220997
221867
|
severity: "warning",
|
|
220998
221868
|
passed: true,
|
|
220999
|
-
message: "No
|
|
221869
|
+
message: "No potential orphan modules detected among registered modules with existing registry paths."
|
|
221000
221870
|
}
|
|
221001
221871
|
];
|
|
221002
221872
|
}
|
|
@@ -221778,7 +222648,10 @@ function filterDependencyGraphSummaryByModuleScope(dependencyGraphSummary, modul
|
|
|
221778
222648
|
)
|
|
221779
222649
|
}));
|
|
221780
222650
|
return {
|
|
221781
|
-
modules: scopedModules
|
|
222651
|
+
modules: scopedModules,
|
|
222652
|
+
hierarchyEdges: dependencyGraphSummary.hierarchyEdges.filter(
|
|
222653
|
+
(edge) => scopeSet.has(edge.parentModule) && scopeSet.has(edge.childModule)
|
|
222654
|
+
)
|
|
221782
222655
|
};
|
|
221783
222656
|
}
|
|
221784
222657
|
async function runArchitectureValidationForWorkspace(workspaceRoot, options) {
|
|
@@ -224779,9 +225652,32 @@ async function readTextFileIfExists4(filePath) {
|
|
|
224779
225652
|
return void 0;
|
|
224780
225653
|
}
|
|
224781
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
|
+
]);
|
|
224782
225675
|
function sortUnique3(values) {
|
|
224783
225676
|
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
224784
225677
|
}
|
|
225678
|
+
function toSafeContractModuleFileStem2(moduleName) {
|
|
225679
|
+
return normalizePath5(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
225680
|
+
}
|
|
224785
225681
|
function getModuleRegistry(contract) {
|
|
224786
225682
|
const modules = contract?.modules;
|
|
224787
225683
|
if (!modules || typeof modules !== "object" || Array.isArray(modules)) {
|
|
@@ -224792,6 +225688,83 @@ function getModuleRegistry(contract) {
|
|
|
224792
225688
|
function normalizePublicEntrypointList(entries) {
|
|
224793
225689
|
return sortUnique3(entries.map((entry) => normalizePath5(entry.trim())).filter((entry) => entry.length > 0));
|
|
224794
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
|
+
}
|
|
224795
225768
|
async function loadModuleContractPublicEntrypoints(workspaceRoot, contractPath) {
|
|
224796
225769
|
const fullContractPath = path26.join(workspaceRoot, ...contractPath.split("/"));
|
|
224797
225770
|
const content = await readTextFileIfExists4(fullContractPath);
|
|
@@ -224832,7 +225805,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
224832
225805
|
const discovered = [];
|
|
224833
225806
|
const discoveredNames = /* @__PURE__ */ new Set();
|
|
224834
225807
|
const toContractPath = (moduleName, registryEntry) => normalizePath5(
|
|
224835
|
-
typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
|
|
225808
|
+
typeof registryEntry?.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
|
|
224836
225809
|
);
|
|
224837
225810
|
const resolveMissingPublicEntrypointAlignment = async (registryEntry, contractPath) => {
|
|
224838
225811
|
if (!Array.isArray(registryEntry?.publicEntrypoints)) {
|
|
@@ -224876,7 +225849,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
224876
225849
|
continue;
|
|
224877
225850
|
}
|
|
224878
225851
|
const contractPath = normalizePath5(
|
|
224879
|
-
typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${moduleName}.contract.json`
|
|
225852
|
+
typeof registryEntry.contract === "string" ? registryEntry.contract : `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`
|
|
224880
225853
|
);
|
|
224881
225854
|
const contractExists = await pathExists10(path26.join(workspaceRoot, ...contractPath.split("/")));
|
|
224882
225855
|
const missingPublicEntrypointAlignment = await resolveMissingPublicEntrypointAlignment(
|
|
@@ -224891,6 +225864,7 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
224891
225864
|
...resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, normalizedSourcePath, contract) } : {},
|
|
224892
225865
|
contractPath,
|
|
224893
225866
|
contractExists,
|
|
225867
|
+
hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
|
|
224894
225868
|
...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
|
|
224895
225869
|
});
|
|
224896
225870
|
discoveredNames.add(moduleName);
|
|
@@ -224915,18 +225889,56 @@ async function discoverModulesFromArchitectureContract(workspaceRoot, contract,
|
|
|
224915
225889
|
...resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) ? { scope: resolveModuleScopeForPath(workspaceRoot, sourcePath, contract) } : {},
|
|
224916
225890
|
contractPath,
|
|
224917
225891
|
contractExists,
|
|
225892
|
+
hasPublicEntrypoint: hasConfiguredPublicEntrypoint(registryEntry),
|
|
224918
225893
|
...missingPublicEntrypointAlignment !== void 0 ? { missingPublicEntrypointAlignment } : {}
|
|
224919
225894
|
});
|
|
224920
225895
|
discoveredNames.add(moduleName);
|
|
224921
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
|
+
}
|
|
224922
225921
|
return discovered.sort((left, right) => left.moduleName.localeCompare(right.moduleName));
|
|
224923
225922
|
}
|
|
224924
225923
|
async function discoverModulesFromFallbackScan(workspaceRoot, modulesRoot = "src/modules", contractsRoot = ".archpilot/contracts") {
|
|
224925
|
-
const
|
|
225924
|
+
const hierarchicalModules = await discoverHierarchicalModuleRoots(workspaceRoot, modulesRoot);
|
|
225925
|
+
const moduleNames = hierarchicalModules.length > 0 ? [] : await listModuleDirectoryNames(workspaceRoot, modulesRoot);
|
|
224926
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
|
+
}
|
|
224927
225939
|
for (const moduleName of moduleNames) {
|
|
224928
225940
|
const sourcePath = `${modulesRoot}/${moduleName}`;
|
|
224929
|
-
const contractPath = `${contractsRoot}/${moduleName}.contract.json`;
|
|
225941
|
+
const contractPath = `${contractsRoot}/${toSafeContractModuleFileStem2(moduleName)}.contract.json`;
|
|
224930
225942
|
discovered.push({
|
|
224931
225943
|
moduleName,
|
|
224932
225944
|
sourcePath,
|
|
@@ -225024,14 +226036,39 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225024
226036
|
moduleFilter: moduleIds
|
|
225025
226037
|
});
|
|
225026
226038
|
const edges = dependencySnapshot.edges.map((edge) => toArchitectureMapEdge(edge));
|
|
226039
|
+
const hierarchyEdges = dependencySnapshot.hierarchyEdges;
|
|
225027
226040
|
const parsedCycles = dependencySnapshot.cycles.map((cycle) => parseCycleString(cycle)).filter((cycle) => cycle.length > 1).sort((left, right) => left.join("->").localeCompare(right.join("->")));
|
|
225028
226041
|
const incomingMap = buildIncomingMap(modules, edges);
|
|
225029
226042
|
const outgoingMap = buildOutgoingMap(modules, edges);
|
|
225030
226043
|
const cycleMembership = new Set(parsedCycles.flatMap((cycle) => cycle));
|
|
225031
|
-
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) => {
|
|
225032
226056
|
const incomingDependencyCount = incomingMap.get(moduleEntry.moduleName)?.length ?? 0;
|
|
225033
226057
|
const outgoingDependencyCount = outgoingMap.get(moduleEntry.moduleName)?.length ?? 0;
|
|
225034
|
-
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
|
+
}
|
|
225035
226072
|
return {
|
|
225036
226073
|
moduleName: moduleEntry.moduleName,
|
|
225037
226074
|
incomingDependencyCount,
|
|
@@ -225043,7 +226080,7 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225043
226080
|
missingPublicEntrypointAlignment: moduleEntry.missingPublicEntrypointAlignment
|
|
225044
226081
|
} : {}
|
|
225045
226082
|
};
|
|
225046
|
-
});
|
|
226083
|
+
}));
|
|
225047
226084
|
const mostDependedOnModules = sortRankedCounts(
|
|
225048
226085
|
hotspotSummary.filter((entry) => entry.incomingDependencyCount > 0).map((entry) => ({
|
|
225049
226086
|
moduleName: entry.moduleName,
|
|
@@ -225059,9 +226096,9 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225059
226096
|
const modulesInCircularDependencies = sortUnique3(
|
|
225060
226097
|
hotspotSummary.filter((entry) => entry.participatesInCycle).map((entry) => entry.moduleName)
|
|
225061
226098
|
);
|
|
225062
|
-
const
|
|
225063
|
-
|
|
225064
|
-
|
|
226099
|
+
const independentModules = sortUnique3(independentModuleNames);
|
|
226100
|
+
const potentialOrphanModules = sortUnique3(potentialOrphanModuleNames);
|
|
226101
|
+
const orphanModules = potentialOrphanModules;
|
|
225065
226102
|
const missingContracts = sortUnique3(
|
|
225066
226103
|
hotspotSummary.filter((entry) => entry.missingContract).map((entry) => entry.moduleName)
|
|
225067
226104
|
);
|
|
@@ -225084,11 +226121,14 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225084
226121
|
modules,
|
|
225085
226122
|
moduleHotspotSummary: hotspotSummary,
|
|
225086
226123
|
edges,
|
|
226124
|
+
hierarchyEdges,
|
|
225087
226125
|
cycles: parsedCycles,
|
|
225088
226126
|
hotspots: {
|
|
225089
226127
|
mostDependedOnModules,
|
|
225090
226128
|
highestOutgoingFanOutModules,
|
|
225091
226129
|
modulesInCircularDependencies,
|
|
226130
|
+
independentModules,
|
|
226131
|
+
potentialOrphanModules,
|
|
225092
226132
|
orphanModules,
|
|
225093
226133
|
missingContracts,
|
|
225094
226134
|
highlyConnectedModules,
|
|
@@ -225098,6 +226138,8 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225098
226138
|
moduleCount: modules.length,
|
|
225099
226139
|
edgeCount: edges.length,
|
|
225100
226140
|
cycleCount: parsedCycles.length,
|
|
226141
|
+
independentCount: independentModules.length,
|
|
226142
|
+
potentialOrphanCount: potentialOrphanModules.length,
|
|
225101
226143
|
orphanCount: orphanModules.length,
|
|
225102
226144
|
highlyConnectedCount: highlyConnectedModules.length,
|
|
225103
226145
|
missingContractCount: missingContracts.length
|
|
@@ -225113,6 +226155,11 @@ async function generateArchitectureMap(workspaceRoot, options) {
|
|
|
225113
226155
|
importCount: edge.importCount,
|
|
225114
226156
|
publicImportCount: edge.publicImportCount,
|
|
225115
226157
|
nonPublicImportCount: edge.nonPublicImportCount
|
|
226158
|
+
})),
|
|
226159
|
+
hierarchyEdges: hierarchyEdges.map((edge) => ({
|
|
226160
|
+
source: edge.parentModule,
|
|
226161
|
+
target: edge.childModule,
|
|
226162
|
+
relationship: "contains"
|
|
225116
226163
|
}))
|
|
225117
226164
|
},
|
|
225118
226165
|
notes: {
|
|
@@ -230143,6 +231190,9 @@ async function pathExists13(targetPath) {
|
|
|
230143
231190
|
function sortUnique12(values) {
|
|
230144
231191
|
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
230145
231192
|
}
|
|
231193
|
+
function toSafeContractModuleFileStem3(moduleName) {
|
|
231194
|
+
return normalizePath11(moduleName).split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0).join(".");
|
|
231195
|
+
}
|
|
230146
231196
|
function wildcardToRegex(pattern) {
|
|
230147
231197
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
230148
231198
|
return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "u");
|
|
@@ -230224,7 +231274,10 @@ function resolveContractPath(contract, moduleName) {
|
|
|
230224
231274
|
return normalizePath11(configured);
|
|
230225
231275
|
}
|
|
230226
231276
|
return normalizePath11(
|
|
230227
|
-
path44.posix.join(
|
|
231277
|
+
path44.posix.join(
|
|
231278
|
+
resolveContractsRootFromContract(contract),
|
|
231279
|
+
`${toSafeContractModuleFileStem3(moduleName)}.contract.json`
|
|
231280
|
+
)
|
|
230228
231281
|
);
|
|
230229
231282
|
}
|
|
230230
231283
|
function createModuleContract(input2) {
|
|
@@ -230236,6 +231289,23 @@ function createModuleContract(input2) {
|
|
|
230236
231289
|
exposedApis: []
|
|
230237
231290
|
};
|
|
230238
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
|
+
}
|
|
230239
231309
|
function isRegistryEntryDifferent(input2) {
|
|
230240
231310
|
const moduleRegistry = getModuleRegistry2(input2.contract);
|
|
230241
231311
|
const existing = moduleRegistry[input2.moduleName] ?? {};
|
|
@@ -230262,7 +231332,15 @@ async function generateMissingModuleContracts(workspaceRoot) {
|
|
|
230262
231332
|
)) {
|
|
230263
231333
|
const contractPath = resolveContractPath(contract, moduleEntry.moduleName);
|
|
230264
231334
|
const sourcePath = normalizePath11(moduleEntry.sourcePath);
|
|
230265
|
-
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(
|
|
230266
231344
|
workspaceRoot,
|
|
230267
231345
|
sourcePath,
|
|
230268
231346
|
adapter
|
|
@@ -230272,8 +231350,7 @@ async function generateMissingModuleContracts(workspaceRoot) {
|
|
|
230272
231350
|
dependsOn: dependsOnBySource.get(moduleEntry.moduleName) ?? [],
|
|
230273
231351
|
publicEntrypoints
|
|
230274
231352
|
});
|
|
230275
|
-
|
|
230276
|
-
if (await pathExists13(absoluteContractPath)) {
|
|
231353
|
+
if (contractExists) {
|
|
230277
231354
|
skippedExistingContracts.push(contractPath);
|
|
230278
231355
|
} else {
|
|
230279
231356
|
await import_node_fs39.promises.mkdir(path44.dirname(absoluteContractPath), { recursive: true });
|
|
@@ -231635,6 +232712,7 @@ var ignoredDirectoryNames2 = /* @__PURE__ */ new Set([
|
|
|
231635
232712
|
]);
|
|
231636
232713
|
var backendStackIds = /* @__PURE__ */ new Set([
|
|
231637
232714
|
"spring",
|
|
232715
|
+
"express",
|
|
231638
232716
|
"fastapi",
|
|
231639
232717
|
"django",
|
|
231640
232718
|
"flask",
|
|
@@ -231643,7 +232721,11 @@ var backendStackIds = /* @__PURE__ */ new Set([
|
|
|
231643
232721
|
"java",
|
|
231644
232722
|
"python",
|
|
231645
232723
|
"php",
|
|
232724
|
+
"laravel",
|
|
231646
232725
|
"go",
|
|
232726
|
+
"ruby_rails",
|
|
232727
|
+
"kotlin",
|
|
232728
|
+
"kotlin_spring",
|
|
231647
232729
|
"node_typescript",
|
|
231648
232730
|
"node_javascript"
|
|
231649
232731
|
]);
|
|
@@ -231861,6 +232943,7 @@ function buildDetectionContext(workspaceRoot, filePaths, pathsByFileName, extens
|
|
|
231861
232943
|
function toDetectedStack(adapterId) {
|
|
231862
232944
|
switch (adapterId) {
|
|
231863
232945
|
case "node_typescript":
|
|
232946
|
+
case "express":
|
|
231864
232947
|
case "nestjs":
|
|
231865
232948
|
case "java":
|
|
231866
232949
|
case "spring":
|
|
@@ -231870,7 +232953,11 @@ function toDetectedStack(adapterId) {
|
|
|
231870
232953
|
case "dotnet":
|
|
231871
232954
|
case "python":
|
|
231872
232955
|
case "php":
|
|
232956
|
+
case "laravel":
|
|
231873
232957
|
case "go":
|
|
232958
|
+
case "ruby_rails":
|
|
232959
|
+
case "kotlin":
|
|
232960
|
+
case "kotlin_spring":
|
|
231874
232961
|
case "react":
|
|
231875
232962
|
case "nextjs":
|
|
231876
232963
|
case "angular":
|
|
@@ -231891,6 +232978,9 @@ function resolveLanguages(stacks) {
|
|
|
231891
232978
|
if (stacks.includes("spring")) {
|
|
231892
232979
|
languages.add("java");
|
|
231893
232980
|
}
|
|
232981
|
+
if (stacks.includes("kotlin") || stacks.includes("kotlin_spring")) {
|
|
232982
|
+
languages.add("kotlin");
|
|
232983
|
+
}
|
|
231894
232984
|
if (stacks.includes("dotnet")) {
|
|
231895
232985
|
languages.add("csharp");
|
|
231896
232986
|
}
|
|
@@ -231909,6 +232999,12 @@ function resolveLanguages(stacks) {
|
|
|
231909
232999
|
if (stacks.includes("php")) {
|
|
231910
233000
|
languages.add("php");
|
|
231911
233001
|
}
|
|
233002
|
+
if (stacks.includes("laravel")) {
|
|
233003
|
+
languages.add("php");
|
|
233004
|
+
}
|
|
233005
|
+
if (stacks.includes("ruby_rails")) {
|
|
233006
|
+
languages.add("ruby");
|
|
233007
|
+
}
|
|
231912
233008
|
if (stacks.includes("go")) {
|
|
231913
233009
|
languages.add("go");
|
|
231914
233010
|
}
|
|
@@ -231922,6 +233018,12 @@ function resolveLanguages(stacks) {
|
|
|
231922
233018
|
languages.add("typescript");
|
|
231923
233019
|
languages.add("javascript");
|
|
231924
233020
|
}
|
|
233021
|
+
if (stacks.includes("express")) {
|
|
233022
|
+
languages.add("javascript");
|
|
233023
|
+
if (stacks.includes("node_typescript")) {
|
|
233024
|
+
languages.add("typescript");
|
|
233025
|
+
}
|
|
233026
|
+
}
|
|
231925
233027
|
if (stacks.includes("react") || stacks.includes("nextjs") || stacks.includes("angular") || stacks.includes("vue")) {
|
|
231926
233028
|
languages.add(stacks.includes("node_typescript") ? "typescript" : "javascript");
|
|
231927
233029
|
}
|
|
@@ -232394,11 +233496,12 @@ var appRouteReservedDirectoryNames = /* @__PURE__ */ new Set([
|
|
|
232394
233496
|
"common",
|
|
232395
233497
|
"shared-ui"
|
|
232396
233498
|
]);
|
|
232397
|
-
var
|
|
233499
|
+
var sourceFileExtensions3 = /* @__PURE__ */ new Set([
|
|
232398
233500
|
".ts",
|
|
232399
233501
|
".tsx",
|
|
232400
233502
|
".js",
|
|
232401
233503
|
".jsx",
|
|
233504
|
+
".vue",
|
|
232402
233505
|
".java",
|
|
232403
233506
|
".py",
|
|
232404
233507
|
".php",
|
|
@@ -232433,7 +233536,9 @@ var roleFileHints = [
|
|
|
232433
233536
|
"layout.tsx",
|
|
232434
233537
|
"layout.jsx",
|
|
232435
233538
|
"route.ts",
|
|
232436
|
-
"route.js"
|
|
233539
|
+
"route.js",
|
|
233540
|
+
"view.vue",
|
|
233541
|
+
"page.vue"
|
|
232437
233542
|
];
|
|
232438
233543
|
function normalizePath13(value) {
|
|
232439
233544
|
return value.replaceAll("\\", "/");
|
|
@@ -232509,7 +233614,7 @@ function inspectModuleFolder(absolutePath) {
|
|
|
232509
233614
|
continue;
|
|
232510
233615
|
}
|
|
232511
233616
|
const extension = path53.extname(entry.name).toLowerCase();
|
|
232512
|
-
if (
|
|
233617
|
+
if (sourceFileExtensions3.has(extension)) {
|
|
232513
233618
|
hasSourceFiles = true;
|
|
232514
233619
|
sourceFileCount += 1;
|
|
232515
233620
|
}
|
|
@@ -232589,14 +233694,17 @@ function collectModuleCandidatesFromRoot(workspaceRoot, modulesRootRelative, def
|
|
|
232589
233694
|
if (skipFrontendUtilities && frontendUtilityDirectoryNames.has(entry.name.toLowerCase())) {
|
|
232590
233695
|
continue;
|
|
232591
233696
|
}
|
|
232592
|
-
const
|
|
232593
|
-
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;
|
|
232594
233702
|
const moduleAbsolutePath = path53.join(absoluteRoot, entry.name);
|
|
232595
233703
|
const inspected = inspectModuleFolder(moduleAbsolutePath);
|
|
232596
233704
|
const detected = makeDetectedModule(
|
|
232597
233705
|
workspaceRoot,
|
|
232598
233706
|
moduleName,
|
|
232599
|
-
|
|
233707
|
+
normalizePath13(`${modulesRootRelative}/${entry.name}`),
|
|
232600
233708
|
modulesRootRelative,
|
|
232601
233709
|
inspected.hasSourceFiles,
|
|
232602
233710
|
inspected.hasRoleFiles,
|
|
@@ -232657,12 +233765,22 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
232657
233765
|
for (const root of [
|
|
232658
233766
|
"src/modules",
|
|
232659
233767
|
"src/features",
|
|
233768
|
+
"src/domain",
|
|
233769
|
+
"src/domains",
|
|
233770
|
+
"src/application",
|
|
233771
|
+
"src/infrastructure",
|
|
232660
233772
|
"src/app",
|
|
232661
233773
|
"app",
|
|
232662
233774
|
"backend/src/modules",
|
|
232663
233775
|
"backend/src/features",
|
|
232664
233776
|
"frontend/src/modules",
|
|
232665
233777
|
"frontend/src/features",
|
|
233778
|
+
"frontend/src/views",
|
|
233779
|
+
"src/views",
|
|
233780
|
+
"routes",
|
|
233781
|
+
"src/routes",
|
|
233782
|
+
"app/Modules",
|
|
233783
|
+
"app/services",
|
|
232666
233784
|
"services",
|
|
232667
233785
|
"packages",
|
|
232668
233786
|
"libs"
|
|
@@ -232677,8 +233795,13 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
232677
233795
|
continue;
|
|
232678
233796
|
}
|
|
232679
233797
|
for (const candidate of [
|
|
233798
|
+
`apps/${appEntry.name}/src`,
|
|
232680
233799
|
`apps/${appEntry.name}/src/modules`,
|
|
232681
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`,
|
|
232682
233805
|
`apps/${appEntry.name}/app`,
|
|
232683
233806
|
`apps/${appEntry.name}/src/app`
|
|
232684
233807
|
]) {
|
|
@@ -232687,8 +233810,70 @@ function collectExplicitRoots(workspaceRoot) {
|
|
|
232687
233810
|
}
|
|
232688
233811
|
}
|
|
232689
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
|
+
}
|
|
232690
233834
|
return uniqueSorted(roots);
|
|
232691
233835
|
}
|
|
233836
|
+
function collectJavaPackageRoots(workspaceRoot) {
|
|
233837
|
+
const roots = [];
|
|
233838
|
+
for (const sourceRoot of ["src/main/java", "src/main/kotlin", "src"]) {
|
|
233839
|
+
const absoluteSourceRoot = path53.join(workspaceRoot, ...sourceRoot.split("/"));
|
|
233840
|
+
if (!pathExists15(absoluteSourceRoot)) {
|
|
233841
|
+
continue;
|
|
233842
|
+
}
|
|
233843
|
+
const queue = [
|
|
233844
|
+
{ absolutePath: absoluteSourceRoot, relativePath: sourceRoot, depth: 0 }
|
|
233845
|
+
];
|
|
233846
|
+
while (queue.length > 0) {
|
|
233847
|
+
const current = queue.shift();
|
|
233848
|
+
if (!current) {
|
|
233849
|
+
continue;
|
|
233850
|
+
}
|
|
233851
|
+
if (current.depth > 6) {
|
|
233852
|
+
continue;
|
|
233853
|
+
}
|
|
233854
|
+
for (const entry of safeReadDirEntries(current.absolutePath)) {
|
|
233855
|
+
if (!entry.isDirectory() || ignoredDirectoryNames4.has(entry.name)) {
|
|
233856
|
+
continue;
|
|
233857
|
+
}
|
|
233858
|
+
const childRelativePath = normalizePath13(`${current.relativePath}/${entry.name}`);
|
|
233859
|
+
const lowerName = entry.name.toLowerCase();
|
|
233860
|
+
if (lowerName === "modules" || lowerName === "features") {
|
|
233861
|
+
const childCount = safeReadDirEntries(path53.join(current.absolutePath, entry.name)).filter((child) => child.isDirectory() && !ignoredDirectoryNames4.has(child.name)).length;
|
|
233862
|
+
if (childCount >= 2) {
|
|
233863
|
+
roots.push(childRelativePath);
|
|
233864
|
+
}
|
|
233865
|
+
continue;
|
|
233866
|
+
}
|
|
233867
|
+
queue.push({
|
|
233868
|
+
absolutePath: path53.join(current.absolutePath, entry.name),
|
|
233869
|
+
relativePath: childRelativePath,
|
|
233870
|
+
depth: current.depth + 1
|
|
233871
|
+
});
|
|
233872
|
+
}
|
|
233873
|
+
}
|
|
233874
|
+
}
|
|
233875
|
+
return roots;
|
|
233876
|
+
}
|
|
232692
233877
|
function collectFallbackSrcModules(workspaceRoot, context) {
|
|
232693
233878
|
const srcRoot = path53.join(workspaceRoot, "src");
|
|
232694
233879
|
const srcEntries = safeReadDirEntries(srcRoot).filter((entry) => entry.isDirectory() && !ignoredDirectoryNames4.has(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
|
|
@@ -232726,6 +233911,14 @@ function detectModules(workspaceRoot, options) {
|
|
|
232726
233911
|
projectKinds: options?.projectKinds,
|
|
232727
233912
|
stacks: options?.stacks
|
|
232728
233913
|
};
|
|
233914
|
+
if (options?.stacks?.some((stack) => stack === "java" || stack === "spring" || stack === "kotlin" || stack === "kotlin_spring")) {
|
|
233915
|
+
for (const root of collectJavaPackageRoots(workspaceRoot)) {
|
|
233916
|
+
if (!explicitRoots.includes(root)) {
|
|
233917
|
+
explicitRoots.push(root);
|
|
233918
|
+
}
|
|
233919
|
+
}
|
|
233920
|
+
explicitRoots.sort((left, right) => left.localeCompare(right));
|
|
233921
|
+
}
|
|
232729
233922
|
for (const root of explicitRoots) {
|
|
232730
233923
|
if (root.endsWith("/src/modules") || root === "src/modules" || root === "backend/src/modules") {
|
|
232731
233924
|
evidence.push(`Found ${root} directory`);
|
|
@@ -233732,6 +234925,8 @@ function resolveImplementationProfile(input2) {
|
|
|
233732
234925
|
return "frontend-vue";
|
|
233733
234926
|
case "nestjs":
|
|
233734
234927
|
return input2.apiStyleHint === "rest" ? "backend-nestjs-rest" : "backend-nestjs";
|
|
234928
|
+
case "express":
|
|
234929
|
+
return input2.apiStyleHint === "rest" ? "backend-express-rest" : "backend-express";
|
|
233735
234930
|
case "spring":
|
|
233736
234931
|
return input2.apiStyleHint === "rest" ? "backend-spring-rest" : "backend-spring";
|
|
233737
234932
|
case "fastapi":
|
|
@@ -233748,6 +234943,14 @@ function resolveImplementationProfile(input2) {
|
|
|
233748
234943
|
return "backend-python";
|
|
233749
234944
|
case "php":
|
|
233750
234945
|
return "backend-php";
|
|
234946
|
+
case "laravel":
|
|
234947
|
+
return input2.apiStyleHint === "rest" ? "backend-laravel-rest" : "backend-laravel";
|
|
234948
|
+
case "ruby_rails":
|
|
234949
|
+
return input2.apiStyleHint === "rest" ? "backend-rails-rest" : "backend-rails";
|
|
234950
|
+
case "kotlin":
|
|
234951
|
+
return "backend-kotlin";
|
|
234952
|
+
case "kotlin_spring":
|
|
234953
|
+
return input2.apiStyleHint === "rest" ? "backend-kotlin-spring-rest" : "backend-kotlin-spring";
|
|
233751
234954
|
case "go":
|
|
233752
234955
|
return "backend-go";
|
|
233753
234956
|
case "node_typescript":
|
|
@@ -233910,7 +235113,7 @@ function resolveStackSelections(detection, projectKinds) {
|
|
|
233910
235113
|
}
|
|
233911
235114
|
if (projectKinds.includes("library")) {
|
|
233912
235115
|
const selectedLibrary = detection.stacks.selectedAdapterId;
|
|
233913
|
-
if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go") {
|
|
235116
|
+
if (selectedLibrary === "terraform" || selectedLibrary === "ansible" || selectedLibrary === "node_typescript" || selectedLibrary === "python" || selectedLibrary === "go" || selectedLibrary === "kotlin") {
|
|
233914
235117
|
selections.library = selectedLibrary;
|
|
233915
235118
|
}
|
|
233916
235119
|
}
|