viberails 0.5.1 → 0.5.3
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/index.cjs +96 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +96 -36
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -328,11 +328,10 @@ function buildMenuOptions(state, packageCount) {
|
|
|
328
328
|
hint: state.fileNamingValue
|
|
329
329
|
});
|
|
330
330
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
});
|
|
331
|
+
const isMonorepo = packageCount > 0;
|
|
332
|
+
const coverageLabel = isMonorepo ? "Default coverage target" : "Test coverage target";
|
|
333
|
+
const coverageHint = state.testCoverage === 0 ? "0 (disabled)" : isMonorepo ? `${state.testCoverage}% (per-package default)` : `${state.testCoverage}%`;
|
|
334
|
+
options.push({ value: "testCoverage", label: coverageLabel, hint: coverageHint });
|
|
336
335
|
options.push({
|
|
337
336
|
value: "enforceMissingTests",
|
|
338
337
|
label: "Enforce missing tests",
|
|
@@ -342,16 +341,16 @@ function buildMenuOptions(state, packageCount) {
|
|
|
342
341
|
options.push(
|
|
343
342
|
{
|
|
344
343
|
value: "coverageSummaryPath",
|
|
345
|
-
label: "Coverage summary path",
|
|
344
|
+
label: isMonorepo ? "Default coverage summary path" : "Coverage summary path",
|
|
346
345
|
hint: state.coverageSummaryPath
|
|
347
346
|
},
|
|
348
347
|
{
|
|
349
348
|
value: "coverageCommand",
|
|
350
|
-
label: "Coverage command",
|
|
349
|
+
label: isMonorepo ? "Default coverage command" : "Coverage command",
|
|
351
350
|
hint: state.coverageCommand ?? "auto-detect from package.json test runner"
|
|
352
351
|
}
|
|
353
352
|
);
|
|
354
|
-
if (
|
|
353
|
+
if (isMonorepo) {
|
|
355
354
|
options.push({
|
|
356
355
|
value: "packageOverrides",
|
|
357
356
|
label: "Per-package coverage overrides",
|
|
@@ -1655,6 +1654,58 @@ function displayRulesPreview(config) {
|
|
|
1655
1654
|
);
|
|
1656
1655
|
console.log("");
|
|
1657
1656
|
}
|
|
1657
|
+
function displayInitSummary(config, exemptedPackages) {
|
|
1658
|
+
const root = config.packages.find((p) => p.path === ".") ?? config.packages[0];
|
|
1659
|
+
const isMonorepo = config.packages.length > 1;
|
|
1660
|
+
const ok = chalk4.green("\u2713");
|
|
1661
|
+
const off = chalk4.dim("\u25CB");
|
|
1662
|
+
console.log("");
|
|
1663
|
+
console.log(` ${chalk4.bold("Rules to apply:")}`);
|
|
1664
|
+
console.log(` ${ok} Max file size: ${chalk4.cyan(`${config.rules.maxFileLines} lines`)}`);
|
|
1665
|
+
const fileNaming = root?.conventions?.fileNaming ?? config.packages.find((p) => p.conventions?.fileNaming)?.conventions?.fileNaming;
|
|
1666
|
+
if (config.rules.enforceNaming && fileNaming) {
|
|
1667
|
+
console.log(` ${ok} File naming: ${chalk4.cyan(fileNaming)}`);
|
|
1668
|
+
} else {
|
|
1669
|
+
console.log(` ${off} File naming: ${chalk4.dim("not enforced")}`);
|
|
1670
|
+
}
|
|
1671
|
+
const testPattern = root?.structure?.testPattern ?? config.packages.find((p) => p.structure?.testPattern)?.structure?.testPattern;
|
|
1672
|
+
if (config.rules.enforceMissingTests && testPattern) {
|
|
1673
|
+
console.log(` ${ok} Missing tests: ${chalk4.cyan(`enforced (${testPattern})`)}`);
|
|
1674
|
+
} else if (config.rules.enforceMissingTests) {
|
|
1675
|
+
console.log(` ${ok} Missing tests: ${chalk4.cyan("enforced")}`);
|
|
1676
|
+
} else {
|
|
1677
|
+
console.log(` ${off} Missing tests: ${chalk4.dim("not enforced")}`);
|
|
1678
|
+
}
|
|
1679
|
+
if (config.rules.testCoverage > 0) {
|
|
1680
|
+
if (isMonorepo) {
|
|
1681
|
+
const withCoverage = config.packages.filter(
|
|
1682
|
+
(p) => (p.rules?.testCoverage ?? config.rules.testCoverage) > 0
|
|
1683
|
+
);
|
|
1684
|
+
console.log(
|
|
1685
|
+
` ${ok} Coverage: ${chalk4.cyan(`${config.rules.testCoverage}%`)} default ${chalk4.dim(`(${withCoverage.length}/${config.packages.length} packages)`)}`
|
|
1686
|
+
);
|
|
1687
|
+
} else {
|
|
1688
|
+
console.log(` ${ok} Coverage: ${chalk4.cyan(`${config.rules.testCoverage}%`)}`);
|
|
1689
|
+
}
|
|
1690
|
+
} else {
|
|
1691
|
+
console.log(` ${off} Coverage: ${chalk4.dim("disabled")}`);
|
|
1692
|
+
}
|
|
1693
|
+
if (exemptedPackages.length > 0) {
|
|
1694
|
+
console.log(
|
|
1695
|
+
` ${chalk4.dim(" exempted:")} ${chalk4.dim(exemptedPackages.join(", "))} ${chalk4.dim("(types-only)")}`
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
if (isMonorepo) {
|
|
1699
|
+
console.log(
|
|
1700
|
+
`
|
|
1701
|
+
${chalk4.dim(`${config.packages.length} packages scanned \xB7 warns on violation \xB7 use --enforce in CI`)}`
|
|
1702
|
+
);
|
|
1703
|
+
} else {
|
|
1704
|
+
console.log(`
|
|
1705
|
+
${chalk4.dim("warns on violation \xB7 use --enforce in CI to block")}`);
|
|
1706
|
+
}
|
|
1707
|
+
console.log("");
|
|
1708
|
+
}
|
|
1658
1709
|
|
|
1659
1710
|
// src/display-text.ts
|
|
1660
1711
|
function plainConfidenceLabel(convention) {
|
|
@@ -1695,10 +1746,13 @@ function formatConventionsText(scanResult) {
|
|
|
1695
1746
|
}
|
|
1696
1747
|
function formatRulesText(config) {
|
|
1697
1748
|
const root = config.packages.find((p) => p.path === ".") ?? config.packages[0];
|
|
1749
|
+
const isMonorepo = config.packages.length > 1;
|
|
1698
1750
|
const lines = [];
|
|
1699
1751
|
lines.push(`Max file size: ${config.rules.maxFileLines} lines`);
|
|
1700
1752
|
if (config.rules.testCoverage > 0) {
|
|
1701
|
-
|
|
1753
|
+
const label = isMonorepo ? "Default coverage target" : "Test coverage target";
|
|
1754
|
+
const suffix = isMonorepo ? " (per-package)" : "";
|
|
1755
|
+
lines.push(`${label}: ${config.rules.testCoverage}%${suffix}`);
|
|
1702
1756
|
} else {
|
|
1703
1757
|
lines.push("Test coverage target: disabled");
|
|
1704
1758
|
}
|
|
@@ -2414,30 +2468,35 @@ import * as path14 from "path";
|
|
|
2414
2468
|
import * as clack7 from "@clack/prompts";
|
|
2415
2469
|
import chalk8 from "chalk";
|
|
2416
2470
|
function checkCoveragePrereqs(projectRoot, scanResult) {
|
|
2417
|
-
const testRunner = scanResult.stack.testRunner;
|
|
2418
|
-
if (!testRunner) return [];
|
|
2419
|
-
const runner = testRunner.name;
|
|
2420
2471
|
const pm = scanResult.stack.packageManager.name;
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
|
|
2431
|
-
reason: "Required for coverage percentage checks with vitest"
|
|
2432
|
-
}
|
|
2433
|
-
];
|
|
2472
|
+
const vitestPackages = scanResult.packages.filter((pkg) => pkg.stack.testRunner?.name === "vitest").map((pkg) => pkg.relativePath);
|
|
2473
|
+
const hasVitest = vitestPackages.length > 0 || scanResult.stack.testRunner?.name === "vitest";
|
|
2474
|
+
if (!hasVitest) return [];
|
|
2475
|
+
let installed = hasDependency(projectRoot, "@vitest/coverage-v8") || hasDependency(projectRoot, "@vitest/coverage-istanbul");
|
|
2476
|
+
if (!installed && vitestPackages.length > 0) {
|
|
2477
|
+
installed = vitestPackages.every((rel) => {
|
|
2478
|
+
const pkgDir = path14.join(projectRoot, rel);
|
|
2479
|
+
return hasDependency(pkgDir, "@vitest/coverage-v8") || hasDependency(pkgDir, "@vitest/coverage-istanbul");
|
|
2480
|
+
});
|
|
2434
2481
|
}
|
|
2435
|
-
|
|
2482
|
+
const addCmd = pm === "yarn" ? "yarn add -D" : pm === "npm" ? "npm install -D" : `${pm} add -D`;
|
|
2483
|
+
const affectedPackages = vitestPackages.length > 1 ? vitestPackages : void 0;
|
|
2484
|
+
const reason = affectedPackages ? `Required for coverage in: ${affectedPackages.join(", ")}` : "Required for coverage percentage checks with vitest";
|
|
2485
|
+
return [
|
|
2486
|
+
{
|
|
2487
|
+
label: "@vitest/coverage-v8",
|
|
2488
|
+
installed,
|
|
2489
|
+
installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
|
|
2490
|
+
reason,
|
|
2491
|
+
affectedPackages
|
|
2492
|
+
}
|
|
2493
|
+
];
|
|
2436
2494
|
}
|
|
2437
2495
|
function displayMissingPrereqs(prereqs) {
|
|
2438
2496
|
const missing = prereqs.filter((p) => !p.installed);
|
|
2439
2497
|
for (const m of missing) {
|
|
2440
|
-
|
|
2498
|
+
const suffix = m.affectedPackages ? ` \u2014 needed for coverage in: ${m.affectedPackages.join(", ")}` : ` \u2014 ${m.reason}`;
|
|
2499
|
+
console.log(` ${chalk8.yellow("!")} ${m.label} not installed${suffix}`);
|
|
2441
2500
|
if (m.installCommand) {
|
|
2442
2501
|
console.log(` Install: ${chalk8.cyan(m.installCommand)}`);
|
|
2443
2502
|
}
|
|
@@ -2446,15 +2505,19 @@ function displayMissingPrereqs(prereqs) {
|
|
|
2446
2505
|
async function promptMissingPrereqs(projectRoot, prereqs) {
|
|
2447
2506
|
const missing = prereqs.filter((p) => !p.installed);
|
|
2448
2507
|
if (missing.length === 0) return { disableCoverage: false };
|
|
2449
|
-
const prereqLines = prereqs.map(
|
|
2450
|
-
(p
|
|
2451
|
-
|
|
2508
|
+
const prereqLines = prereqs.map((p) => {
|
|
2509
|
+
if (p.installed) return `\u2713 ${p.label}`;
|
|
2510
|
+
const detail = p.affectedPackages ? `needed by: ${p.affectedPackages.join(", ")}` : p.reason;
|
|
2511
|
+
return `\u2717 ${p.label} \u2014 ${detail}`;
|
|
2512
|
+
}).join("\n");
|
|
2452
2513
|
clack7.note(prereqLines, "Coverage prerequisites");
|
|
2453
2514
|
let disableCoverage = false;
|
|
2454
2515
|
for (const m of missing) {
|
|
2455
2516
|
if (!m.installCommand) continue;
|
|
2517
|
+
const pkgCount = m.affectedPackages?.length;
|
|
2518
|
+
const message = pkgCount ? `${m.label} is not installed. Required for coverage in ${pkgCount} packages using vitest.` : `${m.label} is not installed. It is required for coverage percentage checks.`;
|
|
2456
2519
|
const choice = await clack7.select({
|
|
2457
|
-
message
|
|
2520
|
+
message,
|
|
2458
2521
|
options: [
|
|
2459
2522
|
{
|
|
2460
2523
|
value: "install",
|
|
@@ -2946,11 +3009,8 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
2946
3009
|
);
|
|
2947
3010
|
}
|
|
2948
3011
|
clack8.note(formatScanResultsText(scanResult), "Scan results");
|
|
2949
|
-
const rulesLines = formatRulesText(config);
|
|
2950
3012
|
const exemptedPkgs = getExemptedPackages(config);
|
|
2951
|
-
|
|
2952
|
-
rulesLines.push(`Auto-exempted from coverage: ${exemptedPkgs.join(", ")} (types-only)`);
|
|
2953
|
-
clack8.note(rulesLines.join("\n"), "Rules");
|
|
3013
|
+
displayInitSummary(config, exemptedPkgs);
|
|
2954
3014
|
const decision = await promptInitDecision();
|
|
2955
3015
|
if (decision === "customize") {
|
|
2956
3016
|
const rootPkg = config.packages.find((p) => p.path === ".") ?? config.packages[0];
|
|
@@ -3132,7 +3192,7 @@ ${chalk12.bold("Synced:")}`);
|
|
|
3132
3192
|
}
|
|
3133
3193
|
|
|
3134
3194
|
// src/index.ts
|
|
3135
|
-
var VERSION = "0.5.
|
|
3195
|
+
var VERSION = "0.5.3";
|
|
3136
3196
|
var program = new Command();
|
|
3137
3197
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
3138
3198
|
program.command("init", { isDefault: true }).description("Scan your project and set up enforcement guardrails").option("-y, --yes", "Non-interactive mode (use defaults, high-confidence only)").option("-f, --force", "Re-initialize, replacing existing config").action(async (options) => {
|