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.js
CHANGED
|
@@ -234,6 +234,10 @@ var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
234
234
|
".svelte-kit",
|
|
235
235
|
".turbo",
|
|
236
236
|
"coverage",
|
|
237
|
+
"public",
|
|
238
|
+
"vendor",
|
|
239
|
+
"__generated__",
|
|
240
|
+
"generated",
|
|
237
241
|
".viberails"
|
|
238
242
|
]);
|
|
239
243
|
var SOURCE_EXTS = /* @__PURE__ */ new Set([
|
|
@@ -255,12 +259,19 @@ var NAMING_PATTERNS = {
|
|
|
255
259
|
};
|
|
256
260
|
function isIgnored(relPath, ignorePatterns) {
|
|
257
261
|
for (const pattern of ignorePatterns) {
|
|
258
|
-
|
|
262
|
+
const startsGlob = pattern.startsWith("**/");
|
|
263
|
+
const endsGlob = pattern.endsWith("/**");
|
|
264
|
+
if (startsGlob && endsGlob) {
|
|
265
|
+
const middle = pattern.slice(3, -3);
|
|
266
|
+
if (relPath.startsWith(`${middle}/`) || relPath.includes(`/${middle}/`) || relPath === middle) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
} else if (endsGlob) {
|
|
259
270
|
const prefix = pattern.slice(0, -3);
|
|
260
271
|
if (relPath.startsWith(`${prefix}/`) || relPath === prefix) return true;
|
|
261
|
-
} else if (
|
|
272
|
+
} else if (startsGlob) {
|
|
262
273
|
const suffix = pattern.slice(3);
|
|
263
|
-
if (relPath.endsWith(suffix)) return true;
|
|
274
|
+
if (relPath.endsWith(suffix) || relPath === suffix) return true;
|
|
264
275
|
} else if (relPath === pattern || relPath.startsWith(`${pattern}/`)) {
|
|
265
276
|
return true;
|
|
266
277
|
}
|
|
@@ -380,13 +391,13 @@ function checkMissingTests(projectRoot, config, severity) {
|
|
|
380
391
|
const testSuffix = testPattern.replace("*", "");
|
|
381
392
|
const sourceFiles = collectSourceFiles(srcPath, projectRoot);
|
|
382
393
|
for (const relFile of sourceFiles) {
|
|
383
|
-
const
|
|
384
|
-
if (
|
|
394
|
+
const basename7 = path5.basename(relFile);
|
|
395
|
+
if (basename7.includes(".test.") || basename7.includes(".spec.") || basename7.startsWith("index.") || basename7.endsWith(".d.ts")) {
|
|
385
396
|
continue;
|
|
386
397
|
}
|
|
387
|
-
const ext = path5.extname(
|
|
398
|
+
const ext = path5.extname(basename7);
|
|
388
399
|
if (!SOURCE_EXTS2.has(ext)) continue;
|
|
389
|
-
const stem =
|
|
400
|
+
const stem = basename7.slice(0, basename7.indexOf("."));
|
|
390
401
|
const expectedTestFile = `${stem}${testSuffix}`;
|
|
391
402
|
const dir = path5.dirname(path5.join(projectRoot, relFile));
|
|
392
403
|
const colocatedTest = path5.join(dir, expectedTestFile);
|
|
@@ -407,6 +418,50 @@ function checkMissingTests(projectRoot, config, severity) {
|
|
|
407
418
|
|
|
408
419
|
// src/commands/check.ts
|
|
409
420
|
var CONFIG_FILE2 = "viberails.config.json";
|
|
421
|
+
function isTestFile(relPath) {
|
|
422
|
+
const filename = path6.basename(relPath);
|
|
423
|
+
return filename.includes(".test.") || filename.includes(".spec.") || filename.startsWith("test.") || filename.startsWith("spec.") || relPath.includes("__tests__/") || relPath.includes("__test__/");
|
|
424
|
+
}
|
|
425
|
+
function printGroupedViolations(violations, limit) {
|
|
426
|
+
const groups = /* @__PURE__ */ new Map();
|
|
427
|
+
for (const v of violations) {
|
|
428
|
+
const existing = groups.get(v.rule) ?? [];
|
|
429
|
+
existing.push(v);
|
|
430
|
+
groups.set(v.rule, existing);
|
|
431
|
+
}
|
|
432
|
+
const ruleOrder = ["file-size", "file-naming", "missing-test", "boundary-violation"];
|
|
433
|
+
const sortedKeys = [...groups.keys()].sort(
|
|
434
|
+
(a, b) => (ruleOrder.indexOf(a) === -1 ? 99 : ruleOrder.indexOf(a)) - (ruleOrder.indexOf(b) === -1 ? 99 : ruleOrder.indexOf(b))
|
|
435
|
+
);
|
|
436
|
+
let totalShown = 0;
|
|
437
|
+
const totalLimit = limit ?? Number.POSITIVE_INFINITY;
|
|
438
|
+
for (const rule of sortedKeys) {
|
|
439
|
+
const group = groups.get(rule);
|
|
440
|
+
if (!group) continue;
|
|
441
|
+
const remaining = totalLimit - totalShown;
|
|
442
|
+
if (remaining <= 0) break;
|
|
443
|
+
const toShow = group.slice(0, remaining);
|
|
444
|
+
const hidden = group.length - toShow.length;
|
|
445
|
+
for (const v of toShow) {
|
|
446
|
+
const icon = v.severity === "error" ? chalk2.red("\u2717") : chalk2.yellow("!");
|
|
447
|
+
console.log(`${icon} ${chalk2.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
448
|
+
}
|
|
449
|
+
totalShown += toShow.length;
|
|
450
|
+
if (hidden > 0) {
|
|
451
|
+
console.log(chalk2.dim(` ... and ${hidden} more ${rule} violations`));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function printSummary(violations) {
|
|
456
|
+
const counts = /* @__PURE__ */ new Map();
|
|
457
|
+
for (const v of violations) {
|
|
458
|
+
counts.set(v.rule, (counts.get(v.rule) ?? 0) + 1);
|
|
459
|
+
}
|
|
460
|
+
const word = violations.length === 1 ? "violation" : "violations";
|
|
461
|
+
const parts = [...counts.entries()].map(([rule, count]) => `${count} ${rule}`);
|
|
462
|
+
console.log(`
|
|
463
|
+
${violations.length} ${word} found (${parts.join(", ")}).`);
|
|
464
|
+
}
|
|
410
465
|
async function checkCommand(options, cwd) {
|
|
411
466
|
const startDir = cwd ?? process.cwd();
|
|
412
467
|
const projectRoot = findProjectRoot(startDir);
|
|
@@ -443,13 +498,15 @@ async function checkCommand(options, cwd) {
|
|
|
443
498
|
if (isIgnored(relPath, effectiveIgnore)) continue;
|
|
444
499
|
if (!fs6.existsSync(absPath)) continue;
|
|
445
500
|
const resolved = resolveConfigForFile(relPath, config);
|
|
446
|
-
|
|
501
|
+
const testFile = isTestFile(relPath);
|
|
502
|
+
const maxLines = testFile ? resolved.rules.maxTestFileLines : resolved.rules.maxFileLines;
|
|
503
|
+
if (maxLines > 0) {
|
|
447
504
|
const lines = countFileLines(absPath);
|
|
448
|
-
if (lines !== null && lines >
|
|
505
|
+
if (lines !== null && lines > maxLines) {
|
|
449
506
|
violations.push({
|
|
450
507
|
file: relPath,
|
|
451
508
|
rule: "file-size",
|
|
452
|
-
message: `${lines} lines (max ${
|
|
509
|
+
message: `${lines} lines (max ${maxLines}). Split into focused modules.`,
|
|
453
510
|
severity
|
|
454
511
|
});
|
|
455
512
|
}
|
|
@@ -497,13 +554,10 @@ async function checkCommand(options, cwd) {
|
|
|
497
554
|
console.log(`${chalk2.green("\u2713")} ${filesToCheck.length} files checked \u2014 no violations`);
|
|
498
555
|
return 0;
|
|
499
556
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
console.log(`${icon} ${chalk2.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
557
|
+
if (!options.quiet) {
|
|
558
|
+
printGroupedViolations(violations, options.limit);
|
|
503
559
|
}
|
|
504
|
-
|
|
505
|
-
console.log(`
|
|
506
|
-
${violations.length} ${word} found.`);
|
|
560
|
+
printSummary(violations);
|
|
507
561
|
if (config.enforcement === "enforce") {
|
|
508
562
|
console.log(chalk2.red("Fix violations before committing."));
|
|
509
563
|
return 1;
|
|
@@ -751,8 +805,8 @@ import * as path9 from "path";
|
|
|
751
805
|
function generateTestStub(sourceRelPath, config, projectRoot) {
|
|
752
806
|
const { testPattern } = config.structure;
|
|
753
807
|
if (!testPattern) return null;
|
|
754
|
-
const
|
|
755
|
-
const stem =
|
|
808
|
+
const basename7 = path9.basename(sourceRelPath);
|
|
809
|
+
const stem = basename7.slice(0, basename7.indexOf("."));
|
|
756
810
|
const testSuffix = testPattern.replace("*", "");
|
|
757
811
|
const testFilename = `${stem}${testSuffix}`;
|
|
758
812
|
const dir = path9.dirname(path9.join(projectRoot, sourceRelPath));
|
|
@@ -1323,7 +1377,7 @@ ${chalk9.bold("Synced:")}`);
|
|
|
1323
1377
|
}
|
|
1324
1378
|
|
|
1325
1379
|
// src/index.ts
|
|
1326
|
-
var VERSION = "0.2.
|
|
1380
|
+
var VERSION = "0.2.3";
|
|
1327
1381
|
var program = new Command();
|
|
1328
1382
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
1329
1383
|
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) => {
|
|
@@ -1344,19 +1398,21 @@ program.command("sync").description("Re-scan and update generated files").action
|
|
|
1344
1398
|
process.exit(1);
|
|
1345
1399
|
}
|
|
1346
1400
|
});
|
|
1347
|
-
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").
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1401
|
+
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(
|
|
1402
|
+
async (options) => {
|
|
1403
|
+
try {
|
|
1404
|
+
const exitCode = await checkCommand({
|
|
1405
|
+
...options,
|
|
1406
|
+
noBoundaries: options.boundaries === false
|
|
1407
|
+
});
|
|
1408
|
+
process.exit(exitCode);
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1411
|
+
console.error(`${chalk10.red("Error:")} ${message}`);
|
|
1412
|
+
process.exit(1);
|
|
1413
|
+
}
|
|
1358
1414
|
}
|
|
1359
|
-
|
|
1415
|
+
);
|
|
1360
1416
|
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) => {
|
|
1361
1417
|
try {
|
|
1362
1418
|
const exitCode = await fixCommand(options);
|