viberails 0.2.2 → 0.2.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 +87 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +87 -31
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -267,6 +267,10 @@ var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
267
267
|
".svelte-kit",
|
|
268
268
|
".turbo",
|
|
269
269
|
"coverage",
|
|
270
|
+
"public",
|
|
271
|
+
"vendor",
|
|
272
|
+
"__generated__",
|
|
273
|
+
"generated",
|
|
270
274
|
".viberails"
|
|
271
275
|
]);
|
|
272
276
|
var SOURCE_EXTS = /* @__PURE__ */ new Set([
|
|
@@ -288,12 +292,19 @@ var NAMING_PATTERNS = {
|
|
|
288
292
|
};
|
|
289
293
|
function isIgnored(relPath, ignorePatterns) {
|
|
290
294
|
for (const pattern of ignorePatterns) {
|
|
291
|
-
|
|
295
|
+
const startsGlob = pattern.startsWith("**/");
|
|
296
|
+
const endsGlob = pattern.endsWith("/**");
|
|
297
|
+
if (startsGlob && endsGlob) {
|
|
298
|
+
const middle = pattern.slice(3, -3);
|
|
299
|
+
if (relPath.startsWith(`${middle}/`) || relPath.includes(`/${middle}/`) || relPath === middle) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
} else if (endsGlob) {
|
|
292
303
|
const prefix = pattern.slice(0, -3);
|
|
293
304
|
if (relPath.startsWith(`${prefix}/`) || relPath === prefix) return true;
|
|
294
|
-
} else if (
|
|
305
|
+
} else if (startsGlob) {
|
|
295
306
|
const suffix = pattern.slice(3);
|
|
296
|
-
if (relPath.endsWith(suffix)) return true;
|
|
307
|
+
if (relPath.endsWith(suffix) || relPath === suffix) return true;
|
|
297
308
|
} else if (relPath === pattern || relPath.startsWith(`${pattern}/`)) {
|
|
298
309
|
return true;
|
|
299
310
|
}
|
|
@@ -413,13 +424,13 @@ function checkMissingTests(projectRoot, config, severity) {
|
|
|
413
424
|
const testSuffix = testPattern.replace("*", "");
|
|
414
425
|
const sourceFiles = collectSourceFiles(srcPath, projectRoot);
|
|
415
426
|
for (const relFile of sourceFiles) {
|
|
416
|
-
const
|
|
417
|
-
if (
|
|
427
|
+
const basename7 = path5.basename(relFile);
|
|
428
|
+
if (basename7.includes(".test.") || basename7.includes(".spec.") || basename7.startsWith("index.") || basename7.endsWith(".d.ts")) {
|
|
418
429
|
continue;
|
|
419
430
|
}
|
|
420
|
-
const ext = path5.extname(
|
|
431
|
+
const ext = path5.extname(basename7);
|
|
421
432
|
if (!SOURCE_EXTS2.has(ext)) continue;
|
|
422
|
-
const stem =
|
|
433
|
+
const stem = basename7.slice(0, basename7.indexOf("."));
|
|
423
434
|
const expectedTestFile = `${stem}${testSuffix}`;
|
|
424
435
|
const dir = path5.dirname(path5.join(projectRoot, relFile));
|
|
425
436
|
const colocatedTest = path5.join(dir, expectedTestFile);
|
|
@@ -440,6 +451,50 @@ function checkMissingTests(projectRoot, config, severity) {
|
|
|
440
451
|
|
|
441
452
|
// src/commands/check.ts
|
|
442
453
|
var CONFIG_FILE2 = "viberails.config.json";
|
|
454
|
+
function isTestFile(relPath) {
|
|
455
|
+
const filename = path6.basename(relPath);
|
|
456
|
+
return filename.includes(".test.") || filename.includes(".spec.") || filename.startsWith("test.") || filename.startsWith("spec.") || relPath.includes("__tests__/") || relPath.includes("__test__/");
|
|
457
|
+
}
|
|
458
|
+
function printGroupedViolations(violations, limit) {
|
|
459
|
+
const groups = /* @__PURE__ */ new Map();
|
|
460
|
+
for (const v of violations) {
|
|
461
|
+
const existing = groups.get(v.rule) ?? [];
|
|
462
|
+
existing.push(v);
|
|
463
|
+
groups.set(v.rule, existing);
|
|
464
|
+
}
|
|
465
|
+
const ruleOrder = ["file-size", "file-naming", "missing-test", "boundary-violation"];
|
|
466
|
+
const sortedKeys = [...groups.keys()].sort(
|
|
467
|
+
(a, b) => (ruleOrder.indexOf(a) === -1 ? 99 : ruleOrder.indexOf(a)) - (ruleOrder.indexOf(b) === -1 ? 99 : ruleOrder.indexOf(b))
|
|
468
|
+
);
|
|
469
|
+
let totalShown = 0;
|
|
470
|
+
const totalLimit = limit ?? Number.POSITIVE_INFINITY;
|
|
471
|
+
for (const rule of sortedKeys) {
|
|
472
|
+
const group = groups.get(rule);
|
|
473
|
+
if (!group) continue;
|
|
474
|
+
const remaining = totalLimit - totalShown;
|
|
475
|
+
if (remaining <= 0) break;
|
|
476
|
+
const toShow = group.slice(0, remaining);
|
|
477
|
+
const hidden = group.length - toShow.length;
|
|
478
|
+
for (const v of toShow) {
|
|
479
|
+
const icon = v.severity === "error" ? import_chalk2.default.red("\u2717") : import_chalk2.default.yellow("!");
|
|
480
|
+
console.log(`${icon} ${import_chalk2.default.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
481
|
+
}
|
|
482
|
+
totalShown += toShow.length;
|
|
483
|
+
if (hidden > 0) {
|
|
484
|
+
console.log(import_chalk2.default.dim(` ... and ${hidden} more ${rule} violations`));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function printSummary(violations) {
|
|
489
|
+
const counts = /* @__PURE__ */ new Map();
|
|
490
|
+
for (const v of violations) {
|
|
491
|
+
counts.set(v.rule, (counts.get(v.rule) ?? 0) + 1);
|
|
492
|
+
}
|
|
493
|
+
const word = violations.length === 1 ? "violation" : "violations";
|
|
494
|
+
const parts = [...counts.entries()].map(([rule, count]) => `${count} ${rule}`);
|
|
495
|
+
console.log(`
|
|
496
|
+
${violations.length} ${word} found (${parts.join(", ")}).`);
|
|
497
|
+
}
|
|
443
498
|
async function checkCommand(options, cwd) {
|
|
444
499
|
const startDir = cwd ?? process.cwd();
|
|
445
500
|
const projectRoot = findProjectRoot(startDir);
|
|
@@ -476,13 +531,15 @@ async function checkCommand(options, cwd) {
|
|
|
476
531
|
if (isIgnored(relPath, effectiveIgnore)) continue;
|
|
477
532
|
if (!fs6.existsSync(absPath)) continue;
|
|
478
533
|
const resolved = resolveConfigForFile(relPath, config);
|
|
479
|
-
|
|
534
|
+
const testFile = isTestFile(relPath);
|
|
535
|
+
const maxLines = testFile ? resolved.rules.maxTestFileLines : resolved.rules.maxFileLines;
|
|
536
|
+
if (maxLines > 0) {
|
|
480
537
|
const lines = countFileLines(absPath);
|
|
481
|
-
if (lines !== null && lines >
|
|
538
|
+
if (lines !== null && lines > maxLines) {
|
|
482
539
|
violations.push({
|
|
483
540
|
file: relPath,
|
|
484
541
|
rule: "file-size",
|
|
485
|
-
message: `${lines} lines (max ${
|
|
542
|
+
message: `${lines} lines (max ${maxLines}). Split into focused modules.`,
|
|
486
543
|
severity
|
|
487
544
|
});
|
|
488
545
|
}
|
|
@@ -530,13 +587,10 @@ async function checkCommand(options, cwd) {
|
|
|
530
587
|
console.log(`${import_chalk2.default.green("\u2713")} ${filesToCheck.length} files checked \u2014 no violations`);
|
|
531
588
|
return 0;
|
|
532
589
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
console.log(`${icon} ${import_chalk2.default.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
590
|
+
if (!options.quiet) {
|
|
591
|
+
printGroupedViolations(violations, options.limit);
|
|
536
592
|
}
|
|
537
|
-
|
|
538
|
-
console.log(`
|
|
539
|
-
${violations.length} ${word} found.`);
|
|
593
|
+
printSummary(violations);
|
|
540
594
|
if (config.enforcement === "enforce") {
|
|
541
595
|
console.log(import_chalk2.default.red("Fix violations before committing."));
|
|
542
596
|
return 1;
|
|
@@ -784,8 +838,8 @@ var path9 = __toESM(require("path"), 1);
|
|
|
784
838
|
function generateTestStub(sourceRelPath, config, projectRoot) {
|
|
785
839
|
const { testPattern } = config.structure;
|
|
786
840
|
if (!testPattern) return null;
|
|
787
|
-
const
|
|
788
|
-
const stem =
|
|
841
|
+
const basename7 = path9.basename(sourceRelPath);
|
|
842
|
+
const stem = basename7.slice(0, basename7.indexOf("."));
|
|
789
843
|
const testSuffix = testPattern.replace("*", "");
|
|
790
844
|
const testFilename = `${stem}${testSuffix}`;
|
|
791
845
|
const dir = path9.dirname(path9.join(projectRoot, sourceRelPath));
|
|
@@ -1356,7 +1410,7 @@ ${import_chalk9.default.bold("Synced:")}`);
|
|
|
1356
1410
|
}
|
|
1357
1411
|
|
|
1358
1412
|
// src/index.ts
|
|
1359
|
-
var VERSION = "0.2.
|
|
1413
|
+
var VERSION = "0.2.3";
|
|
1360
1414
|
var program = new import_commander.Command();
|
|
1361
1415
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
1362
1416
|
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)").action(async (options) => {
|
|
@@ -1377,19 +1431,21 @@ program.command("sync").description("Re-scan and update generated files").action
|
|
|
1377
1431
|
process.exit(1);
|
|
1378
1432
|
}
|
|
1379
1433
|
});
|
|
1380
|
-
program.command("check").description("Check files against enforced rules").option("--staged", "Check only staged files (for pre-commit hooks)").option("--files <files...>", "Check specific files").option("--no-boundaries", "Skip boundary checking").
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1434
|
+
program.command("check").description("Check files against enforced rules").option("--staged", "Check only staged files (for pre-commit hooks)").option("--files <files...>", "Check specific files").option("--no-boundaries", "Skip boundary checking").option("--quiet", "Show only summary counts, not individual violations").option("--limit <n>", "Maximum number of violations to display", Number.parseInt).action(
|
|
1435
|
+
async (options) => {
|
|
1436
|
+
try {
|
|
1437
|
+
const exitCode = await checkCommand({
|
|
1438
|
+
...options,
|
|
1439
|
+
noBoundaries: options.boundaries === false
|
|
1440
|
+
});
|
|
1441
|
+
process.exit(exitCode);
|
|
1442
|
+
} catch (err) {
|
|
1443
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1444
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1445
|
+
process.exit(1);
|
|
1446
|
+
}
|
|
1391
1447
|
}
|
|
1392
|
-
|
|
1448
|
+
);
|
|
1393
1449
|
program.command("fix").description("Auto-fix file naming violations and generate missing test stubs").option("--dry-run", "Show planned fixes without applying them").option("--rule <rules...>", "Fix only specific rules (file-naming, missing-test)").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
1394
1450
|
try {
|
|
1395
1451
|
const exitCode = await fixCommand(options);
|