@massu/core 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +368 -56
- package/dist/hooks/session-start.js +271 -53
- package/package.json +1 -1
- package/src/commands/init.ts +159 -2
- package/src/detect/framework-detector.ts +26 -0
- package/src/detect/manifest-registry.ts +261 -0
- package/src/detect/package-detector.ts +162 -62
- package/src/detect/source-dir-detector.ts +7 -0
- package/src/security/registry-pubkey.generated.ts +1 -1
- package/templates/aspnet/massu.config.yaml +5 -1
package/dist/cli.js
CHANGED
|
@@ -3082,6 +3082,167 @@ var init_dist = __esm({
|
|
|
3082
3082
|
}
|
|
3083
3083
|
});
|
|
3084
3084
|
|
|
3085
|
+
// src/detect/manifest-registry.ts
|
|
3086
|
+
var manifest_registry_exports = {};
|
|
3087
|
+
__export(manifest_registry_exports, {
|
|
3088
|
+
getManifestPatterns: () => getManifestPatterns,
|
|
3089
|
+
getManifestRegistry: () => getManifestRegistry,
|
|
3090
|
+
matchManifestPattern: () => matchManifestPattern
|
|
3091
|
+
});
|
|
3092
|
+
function matchManifestPattern(name2, pattern) {
|
|
3093
|
+
if (pattern.startsWith("*")) {
|
|
3094
|
+
const suffix = pattern.slice(1);
|
|
3095
|
+
if (suffix.includes("*")) {
|
|
3096
|
+
throw new Error(
|
|
3097
|
+
`[manifest-registry] pattern "${pattern}" has more than one wildcard. Only "*.<ext>" extension-globs are supported.`
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3100
|
+
return name2.endsWith(suffix);
|
|
3101
|
+
}
|
|
3102
|
+
return name2 === pattern;
|
|
3103
|
+
}
|
|
3104
|
+
function getManifestRegistry() {
|
|
3105
|
+
if (_registryCache !== null) return _registryCache;
|
|
3106
|
+
_registryCache = [
|
|
3107
|
+
{
|
|
3108
|
+
pattern: "package.json",
|
|
3109
|
+
manifestType: "package.json",
|
|
3110
|
+
language: "typescript",
|
|
3111
|
+
runtime: "node",
|
|
3112
|
+
parse: parsePackageJson,
|
|
3113
|
+
signalKey: "packageJson",
|
|
3114
|
+
signalShape: "json"
|
|
3115
|
+
},
|
|
3116
|
+
{
|
|
3117
|
+
pattern: "pyproject.toml",
|
|
3118
|
+
manifestType: "pyproject.toml",
|
|
3119
|
+
language: "python",
|
|
3120
|
+
runtime: "python3",
|
|
3121
|
+
parse: parsePyproject,
|
|
3122
|
+
signalKey: "pyprojectToml",
|
|
3123
|
+
signalShape: "toml"
|
|
3124
|
+
},
|
|
3125
|
+
{
|
|
3126
|
+
pattern: "requirements.txt",
|
|
3127
|
+
manifestType: "requirements.txt",
|
|
3128
|
+
language: "python",
|
|
3129
|
+
runtime: "python3",
|
|
3130
|
+
parse: parseRequirementsTxt,
|
|
3131
|
+
// Captured via pyprojectToml sibling already; no separate signal.
|
|
3132
|
+
signalKey: null,
|
|
3133
|
+
signalShape: "string"
|
|
3134
|
+
},
|
|
3135
|
+
{
|
|
3136
|
+
pattern: "Pipfile",
|
|
3137
|
+
manifestType: "Pipfile",
|
|
3138
|
+
language: "python",
|
|
3139
|
+
runtime: "python3",
|
|
3140
|
+
parse: parsePipfile,
|
|
3141
|
+
// Captured via pyprojectToml sibling already; no separate signal.
|
|
3142
|
+
signalKey: null,
|
|
3143
|
+
signalShape: "string"
|
|
3144
|
+
},
|
|
3145
|
+
{
|
|
3146
|
+
pattern: "Cargo.toml",
|
|
3147
|
+
manifestType: "Cargo.toml",
|
|
3148
|
+
language: "rust",
|
|
3149
|
+
runtime: "cargo",
|
|
3150
|
+
parse: parseCargoToml,
|
|
3151
|
+
signalKey: "cargoToml",
|
|
3152
|
+
signalShape: "toml"
|
|
3153
|
+
},
|
|
3154
|
+
{
|
|
3155
|
+
pattern: "Package.swift",
|
|
3156
|
+
manifestType: "Package.swift",
|
|
3157
|
+
language: "swift",
|
|
3158
|
+
runtime: "xcode",
|
|
3159
|
+
parse: parsePackageSwift,
|
|
3160
|
+
// No AST adapter consumer yet (swift-swiftui doesn't need it).
|
|
3161
|
+
signalKey: null,
|
|
3162
|
+
signalShape: "string"
|
|
3163
|
+
},
|
|
3164
|
+
{
|
|
3165
|
+
pattern: "go.mod",
|
|
3166
|
+
manifestType: "go.mod",
|
|
3167
|
+
language: "go",
|
|
3168
|
+
runtime: "go",
|
|
3169
|
+
parse: parseGoMod,
|
|
3170
|
+
signalKey: "goMod",
|
|
3171
|
+
signalShape: "string"
|
|
3172
|
+
},
|
|
3173
|
+
{
|
|
3174
|
+
pattern: "pom.xml",
|
|
3175
|
+
manifestType: "pom.xml",
|
|
3176
|
+
language: "java",
|
|
3177
|
+
runtime: "jvm",
|
|
3178
|
+
parse: parsePomXml,
|
|
3179
|
+
signalKey: "pomXml",
|
|
3180
|
+
signalShape: "string"
|
|
3181
|
+
},
|
|
3182
|
+
{
|
|
3183
|
+
pattern: "build.gradle",
|
|
3184
|
+
manifestType: "build.gradle",
|
|
3185
|
+
language: "java",
|
|
3186
|
+
runtime: "jvm",
|
|
3187
|
+
parse: parseBuildGradle,
|
|
3188
|
+
signalKey: "gradleBuild",
|
|
3189
|
+
signalShape: "string"
|
|
3190
|
+
},
|
|
3191
|
+
{
|
|
3192
|
+
pattern: "build.gradle.kts",
|
|
3193
|
+
manifestType: "build.gradle",
|
|
3194
|
+
language: "java",
|
|
3195
|
+
runtime: "jvm",
|
|
3196
|
+
parse: parseBuildGradle,
|
|
3197
|
+
signalKey: "gradleBuild",
|
|
3198
|
+
signalShape: "string"
|
|
3199
|
+
},
|
|
3200
|
+
{
|
|
3201
|
+
pattern: "Gemfile",
|
|
3202
|
+
manifestType: "Gemfile",
|
|
3203
|
+
language: "ruby",
|
|
3204
|
+
runtime: "ruby",
|
|
3205
|
+
parse: parseGemfile,
|
|
3206
|
+
signalKey: "gemfile",
|
|
3207
|
+
signalShape: "string"
|
|
3208
|
+
},
|
|
3209
|
+
// Plan 1.5.1 — closes CR-39 violation (1.5.0 init failed for Phoenix
|
|
3210
|
+
// + ASP.NET fixtures). Both rely on AST adapters that already work
|
|
3211
|
+
// in introspect; the gap was solely package-detector unaware of the
|
|
3212
|
+
// manifest filenames.
|
|
3213
|
+
{
|
|
3214
|
+
pattern: "mix.exs",
|
|
3215
|
+
manifestType: "mix.exs",
|
|
3216
|
+
language: "elixir",
|
|
3217
|
+
runtime: "beam",
|
|
3218
|
+
parse: parseMixExs,
|
|
3219
|
+
signalKey: "mixExs",
|
|
3220
|
+
signalShape: "string"
|
|
3221
|
+
},
|
|
3222
|
+
{
|
|
3223
|
+
pattern: "*.csproj",
|
|
3224
|
+
manifestType: "*.csproj",
|
|
3225
|
+
language: "csharp",
|
|
3226
|
+
runtime: "dotnet",
|
|
3227
|
+
parse: parseCsproj,
|
|
3228
|
+
signalKey: "csproj",
|
|
3229
|
+
signalShape: "string"
|
|
3230
|
+
}
|
|
3231
|
+
];
|
|
3232
|
+
return _registryCache;
|
|
3233
|
+
}
|
|
3234
|
+
function getManifestPatterns() {
|
|
3235
|
+
return getManifestRegistry().map((e2) => e2.pattern);
|
|
3236
|
+
}
|
|
3237
|
+
var _registryCache;
|
|
3238
|
+
var init_manifest_registry = __esm({
|
|
3239
|
+
"src/detect/manifest-registry.ts"() {
|
|
3240
|
+
"use strict";
|
|
3241
|
+
init_package_detector();
|
|
3242
|
+
_registryCache = null;
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
|
|
3085
3246
|
// src/detect/package-detector.ts
|
|
3086
3247
|
import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync2, lstatSync, readdirSync as readdirSync3 } from "fs";
|
|
3087
3248
|
import { join as join3, relative as relative2 } from "path";
|
|
@@ -3489,46 +3650,86 @@ function parseGemfile(path, directory, root, _warnings) {
|
|
|
3489
3650
|
manifestType: "Gemfile"
|
|
3490
3651
|
};
|
|
3491
3652
|
}
|
|
3653
|
+
function parseMixExs(path, directory, root, _warnings) {
|
|
3654
|
+
const raw = safeRead(path);
|
|
3655
|
+
if (raw === null) return null;
|
|
3656
|
+
const deps = [];
|
|
3657
|
+
const depPattern = /\{\s*:([a-z][a-z0-9_]*)\s*,/g;
|
|
3658
|
+
let m3;
|
|
3659
|
+
while ((m3 = depPattern.exec(raw)) !== null) {
|
|
3660
|
+
if (!deps.includes(m3[1])) deps.push(m3[1]);
|
|
3661
|
+
}
|
|
3662
|
+
const appMatch = /\bapp\s*:\s*:([a-z][a-z0-9_]*)/.exec(raw);
|
|
3663
|
+
const name2 = appMatch ? appMatch[1] : null;
|
|
3664
|
+
return {
|
|
3665
|
+
path,
|
|
3666
|
+
relativePath: normalizeRelative(root, path),
|
|
3667
|
+
directory,
|
|
3668
|
+
language: "elixir",
|
|
3669
|
+
runtime: "beam",
|
|
3670
|
+
name: name2,
|
|
3671
|
+
version: null,
|
|
3672
|
+
dependencies: deps,
|
|
3673
|
+
devDependencies: [],
|
|
3674
|
+
scripts: [],
|
|
3675
|
+
manifestType: "mix.exs"
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
function parseCsproj(path, directory, root, _warnings) {
|
|
3679
|
+
const raw = safeRead(path);
|
|
3680
|
+
if (raw === null) return null;
|
|
3681
|
+
const deps = [];
|
|
3682
|
+
const pkgRefPattern = /<PackageReference\s+[^>]*Include\s*=\s*"([^"]+)"/gi;
|
|
3683
|
+
let m3;
|
|
3684
|
+
while ((m3 = pkgRefPattern.exec(raw)) !== null) {
|
|
3685
|
+
if (!deps.includes(m3[1])) deps.push(m3[1]);
|
|
3686
|
+
}
|
|
3687
|
+
const sdkMatch = /<Project\s+[^>]*Sdk\s*=\s*"([^"]+)"/i.exec(raw);
|
|
3688
|
+
if (sdkMatch && !deps.includes(sdkMatch[1])) {
|
|
3689
|
+
deps.push(sdkMatch[1]);
|
|
3690
|
+
}
|
|
3691
|
+
const fname = path.split(/[/\\]/).pop() ?? "";
|
|
3692
|
+
const name2 = fname.endsWith(".csproj") ? fname.slice(0, -".csproj".length) : null;
|
|
3693
|
+
return {
|
|
3694
|
+
path,
|
|
3695
|
+
relativePath: normalizeRelative(root, path),
|
|
3696
|
+
directory,
|
|
3697
|
+
language: "csharp",
|
|
3698
|
+
runtime: "dotnet",
|
|
3699
|
+
name: name2,
|
|
3700
|
+
version: null,
|
|
3701
|
+
dependencies: deps,
|
|
3702
|
+
devDependencies: [],
|
|
3703
|
+
scripts: [],
|
|
3704
|
+
manifestType: "*.csproj"
|
|
3705
|
+
};
|
|
3706
|
+
}
|
|
3492
3707
|
function detectManifestsInDir(dir, root, warnings) {
|
|
3708
|
+
const { getManifestRegistry: getManifestRegistry2, matchManifestPattern: matchManifestPattern2 } = manifest_registry_exports;
|
|
3493
3709
|
const out2 = [];
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
if (!
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
break;
|
|
3517
|
-
case "go.mod":
|
|
3518
|
-
m3 = parseGoMod(path, dir, root, warnings);
|
|
3519
|
-
break;
|
|
3520
|
-
case "pom.xml":
|
|
3521
|
-
m3 = parsePomXml(path, dir, root, warnings);
|
|
3522
|
-
break;
|
|
3523
|
-
case "build.gradle":
|
|
3524
|
-
case "build.gradle.kts":
|
|
3525
|
-
m3 = parseBuildGradle(path, dir, root, warnings);
|
|
3526
|
-
break;
|
|
3527
|
-
case "Gemfile":
|
|
3528
|
-
m3 = parseGemfile(path, dir, root, warnings);
|
|
3529
|
-
break;
|
|
3710
|
+
let dirEntries = null;
|
|
3711
|
+
for (const entry of getManifestRegistry2()) {
|
|
3712
|
+
if (!entry.pattern.startsWith("*")) {
|
|
3713
|
+
const path = join3(dir, entry.pattern);
|
|
3714
|
+
if (!existsSync5(path)) continue;
|
|
3715
|
+
const m3 = entry.parse(path, dir, root, warnings);
|
|
3716
|
+
if (m3 !== null) out2.push(m3);
|
|
3717
|
+
} else {
|
|
3718
|
+
if (dirEntries === null) {
|
|
3719
|
+
try {
|
|
3720
|
+
dirEntries = readdirSync3(dir);
|
|
3721
|
+
} catch {
|
|
3722
|
+
dirEntries = [];
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
for (const fname of dirEntries) {
|
|
3726
|
+
if (!matchManifestPattern2(fname, entry.pattern)) continue;
|
|
3727
|
+
const path = join3(dir, fname);
|
|
3728
|
+
if (!existsSync5(path)) continue;
|
|
3729
|
+
const m3 = entry.parse(path, dir, root, warnings);
|
|
3730
|
+
if (m3 !== null) out2.push(m3);
|
|
3731
|
+
}
|
|
3530
3732
|
}
|
|
3531
|
-
if (m3 !== null) out2.push(m3);
|
|
3532
3733
|
}
|
|
3533
3734
|
return out2;
|
|
3534
3735
|
}
|
|
@@ -3562,11 +3763,12 @@ function detectPackageManifests(projectRoot) {
|
|
|
3562
3763
|
}
|
|
3563
3764
|
return { manifests: dedup, warnings };
|
|
3564
3765
|
}
|
|
3565
|
-
var WORKSPACE_DIRS, IGNORED_DIRS
|
|
3766
|
+
var WORKSPACE_DIRS, IGNORED_DIRS;
|
|
3566
3767
|
var init_package_detector = __esm({
|
|
3567
3768
|
"src/detect/package-detector.ts"() {
|
|
3568
3769
|
"use strict";
|
|
3569
3770
|
init_dist();
|
|
3771
|
+
init_manifest_registry();
|
|
3570
3772
|
WORKSPACE_DIRS = ["apps", "packages", "services", "libs", "modules"];
|
|
3571
3773
|
IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
3572
3774
|
"node_modules",
|
|
@@ -3589,19 +3791,6 @@ var init_package_detector = __esm({
|
|
|
3589
3791
|
"DerivedData",
|
|
3590
3792
|
"Pods"
|
|
3591
3793
|
]);
|
|
3592
|
-
MANIFEST_FILES = [
|
|
3593
|
-
"package.json",
|
|
3594
|
-
"pyproject.toml",
|
|
3595
|
-
"requirements.txt",
|
|
3596
|
-
"Pipfile",
|
|
3597
|
-
"Cargo.toml",
|
|
3598
|
-
"Package.swift",
|
|
3599
|
-
"go.mod",
|
|
3600
|
-
"pom.xml",
|
|
3601
|
-
"build.gradle",
|
|
3602
|
-
"build.gradle.kts",
|
|
3603
|
-
"Gemfile"
|
|
3604
|
-
];
|
|
3605
3794
|
}
|
|
3606
3795
|
});
|
|
3607
3796
|
|
|
@@ -3757,6 +3946,13 @@ var init_framework_detector = __esm({
|
|
|
3757
3946
|
{ language: "go", kind: "framework", keyword: "github.com/labstack/echo", value: "echo", priority: 10 },
|
|
3758
3947
|
{ language: "go", kind: "framework", keyword: "github.com/gofiber/fiber", value: "fiber", priority: 10 },
|
|
3759
3948
|
{ language: "go", kind: "framework", keyword: "github.com/go-chi/chi", value: "chi", priority: 9 },
|
|
3949
|
+
// chi versioned import paths (Go convention: github.com/<org>/<name>/v<N>).
|
|
3950
|
+
// matchRule does exact case-insensitive set lookup, so the unversioned and
|
|
3951
|
+
// each major-version path each need their own rule.
|
|
3952
|
+
{ language: "go", kind: "framework", keyword: "github.com/go-chi/chi/v2", value: "chi", priority: 9 },
|
|
3953
|
+
{ language: "go", kind: "framework", keyword: "github.com/go-chi/chi/v3", value: "chi", priority: 9 },
|
|
3954
|
+
{ language: "go", kind: "framework", keyword: "github.com/go-chi/chi/v4", value: "chi", priority: 9 },
|
|
3955
|
+
{ language: "go", kind: "framework", keyword: "github.com/go-chi/chi/v5", value: "chi", priority: 9 },
|
|
3760
3956
|
{ language: "go", kind: "test_framework", keyword: "github.com/stretchr/testify", value: "testify", priority: 8 },
|
|
3761
3957
|
{ language: "go", kind: "orm", keyword: "gorm.io/gorm", value: "gorm", priority: 10 },
|
|
3762
3958
|
// Swift (SPM dependency names, best-effort)
|
|
@@ -3772,7 +3968,26 @@ var init_framework_detector = __esm({
|
|
|
3772
3968
|
{ language: "ruby", kind: "framework", keyword: "rails", value: "rails", priority: 10 },
|
|
3773
3969
|
{ language: "ruby", kind: "framework", keyword: "sinatra", value: "sinatra", priority: 9 },
|
|
3774
3970
|
{ language: "ruby", kind: "test_framework", keyword: "rspec", value: "rspec", priority: 10 },
|
|
3775
|
-
{ language: "ruby", kind: "orm", keyword: "activerecord", value: "activerecord", priority: 10 }
|
|
3971
|
+
{ language: "ruby", kind: "orm", keyword: "activerecord", value: "activerecord", priority: 10 },
|
|
3972
|
+
// Plan 1.5.1: elixir + csharp framework rules. Closes the CR-39 gap
|
|
3973
|
+
// where Phoenix + ASP.NET projects produced `framework.languages.<lang>`
|
|
3974
|
+
// entries WITHOUT a `framework:` value, which prevented variant
|
|
3975
|
+
// templates from being looked up.
|
|
3976
|
+
{ language: "elixir", kind: "framework", keyword: "phoenix", value: "phoenix", priority: 10 },
|
|
3977
|
+
{ language: "elixir", kind: "test_framework", keyword: "ex_unit", value: "ex-unit", priority: 10 },
|
|
3978
|
+
{ language: "elixir", kind: "orm", keyword: "ecto", value: "ecto", priority: 10 },
|
|
3979
|
+
// ASP.NET Core surfaces via several PackageReference names; the canonical
|
|
3980
|
+
// ones in modern .NET projects are .App and .Mvc. matchRule does exact
|
|
3981
|
+
// (case-insensitive) lookup against the deps set parseCsproj extracts.
|
|
3982
|
+
{ language: "csharp", kind: "framework", keyword: "Microsoft.AspNetCore.App", value: "aspnet-core", priority: 10 },
|
|
3983
|
+
{ language: "csharp", kind: "framework", keyword: "Microsoft.AspNetCore.Mvc", value: "aspnet-core", priority: 10 },
|
|
3984
|
+
{ language: "csharp", kind: "framework", keyword: "Microsoft.AspNetCore", value: "aspnet-core", priority: 9 },
|
|
3985
|
+
// SDK-style projects: `<Project Sdk="Microsoft.NET.Sdk.Web">` is the
|
|
3986
|
+
// canonical ASP.NET Core declaration in modern .NET. parseCsproj
|
|
3987
|
+
// surfaces the Sdk attribute as a dep so this rule can match.
|
|
3988
|
+
{ language: "csharp", kind: "framework", keyword: "Microsoft.NET.Sdk.Web", value: "aspnet-core", priority: 10 },
|
|
3989
|
+
{ language: "csharp", kind: "test_framework", keyword: "xunit", value: "xunit", priority: 10 },
|
|
3990
|
+
{ language: "csharp", kind: "orm", keyword: "EntityFrameworkCore", value: "ef-core", priority: 10 }
|
|
3776
3991
|
];
|
|
3777
3992
|
}
|
|
3778
3993
|
});
|
|
@@ -9694,7 +9909,10 @@ var init_source_dir_detector = __esm({
|
|
|
9694
9909
|
swift: ["swift"],
|
|
9695
9910
|
go: ["go"],
|
|
9696
9911
|
java: ["java", "kt"],
|
|
9697
|
-
ruby: ["rb"]
|
|
9912
|
+
ruby: ["rb"],
|
|
9913
|
+
// Plan 1.5.1 — closing CR-39 init gap for Phoenix + ASP.NET projects.
|
|
9914
|
+
elixir: ["ex", "exs"],
|
|
9915
|
+
csharp: ["cs"]
|
|
9698
9916
|
};
|
|
9699
9917
|
TEST_FILE_PATTERNS = {
|
|
9700
9918
|
python: [/_test\.py$/, /test_[^/]*\.py$/],
|
|
@@ -9704,7 +9922,11 @@ var init_source_dir_detector = __esm({
|
|
|
9704
9922
|
swift: [/Tests\//],
|
|
9705
9923
|
go: [/_test\.go$/],
|
|
9706
9924
|
java: [/Test[^/]*\.(java|kt)$/, /[^/]*Test\.(java|kt)$/],
|
|
9707
|
-
ruby: [/_spec\.rb$/, /_test\.rb$/]
|
|
9925
|
+
ruby: [/_spec\.rb$/, /_test\.rb$/],
|
|
9926
|
+
// Phoenix/ExUnit canonical: `test/**_test.exs`. ASP.NET / xUnit
|
|
9927
|
+
// canonical: `*Tests.cs` or `*.Tests/...`.
|
|
9928
|
+
elixir: [/_test\.exs$/, /\/test\//],
|
|
9929
|
+
csharp: [/Tests?\.cs$/, /\.Tests?\//]
|
|
9708
9930
|
};
|
|
9709
9931
|
TEST_DIR_KEYWORDS = ["tests", "test", "__tests__", "spec", "specs"];
|
|
9710
9932
|
}
|
|
@@ -15688,6 +15910,7 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
15688
15910
|
// src/commands/init.ts
|
|
15689
15911
|
var init_exports = {};
|
|
15690
15912
|
__export(init_exports, {
|
|
15913
|
+
applyVariantTemplate: () => applyVariantTemplate,
|
|
15691
15914
|
buildConfigFromDetection: () => buildConfigFromDetection,
|
|
15692
15915
|
buildHooksConfig: () => buildHooksConfig,
|
|
15693
15916
|
copyTemplateConfig: () => copyTemplateConfig,
|
|
@@ -16010,6 +16233,76 @@ function buildConfigFromDetection(opts) {
|
|
|
16010
16233
|
}
|
|
16011
16234
|
return config;
|
|
16012
16235
|
}
|
|
16236
|
+
function applyVariantTemplate(config, templatesDir) {
|
|
16237
|
+
if (!templatesDir) return config;
|
|
16238
|
+
const fw = config.framework;
|
|
16239
|
+
if (!fw) return config;
|
|
16240
|
+
const langs = fw.languages;
|
|
16241
|
+
if (!langs || typeof langs !== "object") return config;
|
|
16242
|
+
let templateId = null;
|
|
16243
|
+
for (const langEntry of Object.values(langs)) {
|
|
16244
|
+
if (langEntry && typeof langEntry === "object") {
|
|
16245
|
+
const fwName = langEntry.framework;
|
|
16246
|
+
if (typeof fwName === "string" && FRAMEWORK_TO_TEMPLATE_ID[fwName]) {
|
|
16247
|
+
templateId = FRAMEWORK_TO_TEMPLATE_ID[fwName];
|
|
16248
|
+
break;
|
|
16249
|
+
}
|
|
16250
|
+
}
|
|
16251
|
+
}
|
|
16252
|
+
if (templateId === null) return config;
|
|
16253
|
+
const templatePath = resolve6(templatesDir, templateId, "massu.config.yaml");
|
|
16254
|
+
if (!existsSync9(templatePath)) return config;
|
|
16255
|
+
let template;
|
|
16256
|
+
try {
|
|
16257
|
+
template = yamlParse(readFileSync7(templatePath, "utf-8"));
|
|
16258
|
+
} catch {
|
|
16259
|
+
return config;
|
|
16260
|
+
}
|
|
16261
|
+
const out2 = { ...config };
|
|
16262
|
+
const tplFw = template.framework;
|
|
16263
|
+
const outFw = out2.framework ?? {};
|
|
16264
|
+
if (tplFw) {
|
|
16265
|
+
if (typeof tplFw.router === "string" && (!outFw.router || outFw.router === "none")) {
|
|
16266
|
+
outFw.router = tplFw.router;
|
|
16267
|
+
}
|
|
16268
|
+
if (typeof tplFw.orm === "string" && (!outFw.orm || outFw.orm === "none")) {
|
|
16269
|
+
outFw.orm = tplFw.orm;
|
|
16270
|
+
}
|
|
16271
|
+
if (typeof tplFw.ui === "string" && (!outFw.ui || outFw.ui === "none")) {
|
|
16272
|
+
outFw.ui = tplFw.ui;
|
|
16273
|
+
}
|
|
16274
|
+
}
|
|
16275
|
+
out2.framework = outFw;
|
|
16276
|
+
const tplPaths = template.paths;
|
|
16277
|
+
const outPaths = out2.paths ?? {};
|
|
16278
|
+
if (tplPaths && typeof tplPaths.source === "string" && tplPaths.source) {
|
|
16279
|
+
outPaths.source = tplPaths.source;
|
|
16280
|
+
}
|
|
16281
|
+
out2.paths = outPaths;
|
|
16282
|
+
const tplVerify = template.verification;
|
|
16283
|
+
const outVerify = out2.verification ?? {};
|
|
16284
|
+
if (tplVerify) {
|
|
16285
|
+
for (const [lang, tplLangVerify] of Object.entries(tplVerify)) {
|
|
16286
|
+
if (!tplLangVerify || typeof tplLangVerify !== "object") continue;
|
|
16287
|
+
const outLangVerify = outVerify[lang] ?? {};
|
|
16288
|
+
for (const key of ["lint", "syntax"]) {
|
|
16289
|
+
if (typeof tplLangVerify[key] === "string" && tplLangVerify[key]) {
|
|
16290
|
+
outLangVerify[key] = tplLangVerify[key];
|
|
16291
|
+
}
|
|
16292
|
+
}
|
|
16293
|
+
for (const key of ["test", "type", "build"]) {
|
|
16294
|
+
if (typeof tplLangVerify[key] === "string" && tplLangVerify[key] && !outLangVerify[key]) {
|
|
16295
|
+
outLangVerify[key] = tplLangVerify[key];
|
|
16296
|
+
}
|
|
16297
|
+
}
|
|
16298
|
+
outVerify[lang] = outLangVerify;
|
|
16299
|
+
}
|
|
16300
|
+
}
|
|
16301
|
+
if (Object.keys(outVerify).length > 0) {
|
|
16302
|
+
out2.verification = outVerify;
|
|
16303
|
+
}
|
|
16304
|
+
return out2;
|
|
16305
|
+
}
|
|
16013
16306
|
function renderConfigYaml(config) {
|
|
16014
16307
|
return `# Massu AI Configuration
|
|
16015
16308
|
# Generated by: npx massu init (schema_version=2, detection-driven)
|
|
@@ -16134,7 +16427,17 @@ function listTemplates() {
|
|
|
16134
16427
|
function resolveTemplatesDir() {
|
|
16135
16428
|
const cwd = process.cwd();
|
|
16136
16429
|
const candidates = [
|
|
16430
|
+
// Project-local install: `<project>/node_modules/@massu/core/templates`.
|
|
16137
16431
|
resolve6(cwd, "node_modules/@massu/core/templates"),
|
|
16432
|
+
// Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
|
|
16433
|
+
// templates live one level up at `<package>/templates`. (Plan 1.5.1
|
|
16434
|
+
// bug discovery: pre-existing layout assumed `dist/commands/init.js`
|
|
16435
|
+
// depth which never matched the bundled cli, so resolveTemplatesDir
|
|
16436
|
+
// returned null in production for both `--template` mode AND the
|
|
16437
|
+
// applyVariantTemplate path.)
|
|
16438
|
+
resolve6(__dirname2, "../templates"),
|
|
16439
|
+
// Legacy nested layouts retained as fallbacks (in case a future
|
|
16440
|
+
// build moves cli.js back into a subdirectory).
|
|
16138
16441
|
resolve6(__dirname2, "../../templates"),
|
|
16139
16442
|
resolve6(__dirname2, "../../../templates")
|
|
16140
16443
|
];
|
|
@@ -16501,7 +16804,8 @@ async function runInit(argv, overrides) {
|
|
|
16501
16804
|
return;
|
|
16502
16805
|
}
|
|
16503
16806
|
}
|
|
16504
|
-
const
|
|
16807
|
+
const baseConfig = buildConfigFromDetection({ projectRoot, detection });
|
|
16808
|
+
const config = applyVariantTemplate(baseConfig, resolveTemplatesDir());
|
|
16505
16809
|
const content = renderConfigYaml(config);
|
|
16506
16810
|
const writeRes = writeConfigAtomic(configPath, content);
|
|
16507
16811
|
if (!writeRes.validated) {
|
|
@@ -16637,7 +16941,7 @@ async function promptStackConfirm() {
|
|
|
16637
16941
|
function capitalize(str) {
|
|
16638
16942
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
16639
16943
|
}
|
|
16640
|
-
var __filename2, __dirname2, TEMPLATE_NAMES;
|
|
16944
|
+
var __filename2, __dirname2, FRAMEWORK_TO_TEMPLATE_ID, TEMPLATE_NAMES;
|
|
16641
16945
|
var init_init = __esm({
|
|
16642
16946
|
"src/commands/init.ts"() {
|
|
16643
16947
|
"use strict";
|
|
@@ -16648,6 +16952,14 @@ var init_init = __esm({
|
|
|
16648
16952
|
init_drift();
|
|
16649
16953
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
16650
16954
|
__dirname2 = dirname4(__filename2);
|
|
16955
|
+
FRAMEWORK_TO_TEMPLATE_ID = {
|
|
16956
|
+
rails: "rails",
|
|
16957
|
+
phoenix: "phoenix",
|
|
16958
|
+
"aspnet-core": "aspnet",
|
|
16959
|
+
"spring-boot": "spring",
|
|
16960
|
+
chi: "go-chi",
|
|
16961
|
+
flask: "python-flask"
|
|
16962
|
+
};
|
|
16651
16963
|
TEMPLATE_NAMES = [
|
|
16652
16964
|
"python-fastapi",
|
|
16653
16965
|
"python-django",
|