@viberails/config 0.1.0
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/LICENSE +21 -0
- package/dist/index.cjs +512 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +314 -0
- package/dist/index.d.ts +314 -0
- package/dist/index.js +468 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Casasola
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
DEFAULT_IGNORE: () => DEFAULT_IGNORE,
|
|
34
|
+
DEFAULT_RULES: () => DEFAULT_RULES,
|
|
35
|
+
VERSION: () => VERSION,
|
|
36
|
+
configSchema: () => configSchema,
|
|
37
|
+
generateConfig: () => generateConfig,
|
|
38
|
+
loadConfig: () => loadConfig,
|
|
39
|
+
loadConfigSafe: () => loadConfigSafe,
|
|
40
|
+
mergeConfig: () => mergeConfig
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
|
+
|
|
44
|
+
// src/defaults.ts
|
|
45
|
+
var DEFAULT_RULES = {
|
|
46
|
+
maxFileLines: 300,
|
|
47
|
+
maxFunctionLines: 50,
|
|
48
|
+
requireTests: true,
|
|
49
|
+
enforceNaming: true,
|
|
50
|
+
enforceBoundaries: false
|
|
51
|
+
};
|
|
52
|
+
var DEFAULT_IGNORE = ["**/*.d.ts", "dist/**", "node_modules/**"];
|
|
53
|
+
|
|
54
|
+
// src/generate-config.ts
|
|
55
|
+
var path = __toESM(require("path"), 1);
|
|
56
|
+
function formatStackItem(item) {
|
|
57
|
+
return item.version ? `${item.name}@${item.version}` : item.name;
|
|
58
|
+
}
|
|
59
|
+
function mapStack(scanResult) {
|
|
60
|
+
const { stack } = scanResult;
|
|
61
|
+
const config = {
|
|
62
|
+
language: formatStackItem(stack.language),
|
|
63
|
+
packageManager: formatStackItem(stack.packageManager)
|
|
64
|
+
};
|
|
65
|
+
if (stack.framework) config.framework = formatStackItem(stack.framework);
|
|
66
|
+
if (stack.styling) config.styling = formatStackItem(stack.styling);
|
|
67
|
+
if (stack.backend) config.backend = formatStackItem(stack.backend);
|
|
68
|
+
if (stack.linter) config.linter = formatStackItem(stack.linter);
|
|
69
|
+
if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
var ROLE_TO_FIELD = {
|
|
73
|
+
pages: "pages",
|
|
74
|
+
components: "components",
|
|
75
|
+
hooks: "hooks",
|
|
76
|
+
utils: "utils",
|
|
77
|
+
types: "types",
|
|
78
|
+
tests: "tests"
|
|
79
|
+
};
|
|
80
|
+
function mapStructure(scanResult) {
|
|
81
|
+
const { structure } = scanResult;
|
|
82
|
+
const config = {};
|
|
83
|
+
if (structure.srcDir) {
|
|
84
|
+
config.srcDir = structure.srcDir;
|
|
85
|
+
}
|
|
86
|
+
for (const dir of structure.directories) {
|
|
87
|
+
const field = ROLE_TO_FIELD[dir.role];
|
|
88
|
+
if (field && config[field] === void 0) {
|
|
89
|
+
config[field] = dir.path;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (structure.testPattern) {
|
|
93
|
+
config.testPattern = structure.testPattern.value;
|
|
94
|
+
}
|
|
95
|
+
return config;
|
|
96
|
+
}
|
|
97
|
+
function mapConvention(convention) {
|
|
98
|
+
if (convention.confidence === "low") {
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
value: convention.value,
|
|
103
|
+
_confidence: convention.confidence,
|
|
104
|
+
_consistency: convention.consistency
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
var CONVENTION_KEYS = [
|
|
108
|
+
"fileNaming",
|
|
109
|
+
"componentNaming",
|
|
110
|
+
"hookNaming",
|
|
111
|
+
"importAlias"
|
|
112
|
+
];
|
|
113
|
+
function mapConventions(scanResult) {
|
|
114
|
+
const config = {};
|
|
115
|
+
for (const key of CONVENTION_KEYS) {
|
|
116
|
+
const detected = scanResult.conventions[key];
|
|
117
|
+
if (detected) {
|
|
118
|
+
const value = mapConvention(detected);
|
|
119
|
+
if (value !== void 0) {
|
|
120
|
+
config[key] = value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return config;
|
|
125
|
+
}
|
|
126
|
+
function generateConfig(scanResult) {
|
|
127
|
+
const config = {
|
|
128
|
+
$schema: "https://viberails.sh/schema/v1.json",
|
|
129
|
+
version: 1,
|
|
130
|
+
name: path.basename(scanResult.root),
|
|
131
|
+
enforcement: "warn",
|
|
132
|
+
stack: mapStack(scanResult),
|
|
133
|
+
structure: mapStructure(scanResult),
|
|
134
|
+
conventions: mapConventions(scanResult),
|
|
135
|
+
rules: { ...DEFAULT_RULES },
|
|
136
|
+
ignore: [...DEFAULT_IGNORE]
|
|
137
|
+
};
|
|
138
|
+
if (scanResult.workspace) {
|
|
139
|
+
config.workspace = {
|
|
140
|
+
packages: scanResult.workspace.packages.map((p) => p.relativePath),
|
|
141
|
+
isMonorepo: true
|
|
142
|
+
};
|
|
143
|
+
config.boundaries = [];
|
|
144
|
+
}
|
|
145
|
+
return config;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/load-config.ts
|
|
149
|
+
var fs = __toESM(require("fs/promises"), 1);
|
|
150
|
+
function validateConfig(parsed, configPath) {
|
|
151
|
+
const required = ["version", "name", "stack", "rules"];
|
|
152
|
+
const missing = required.filter((field) => parsed[field] === void 0);
|
|
153
|
+
if (missing.length > 0) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(", ")}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function loadConfig(configPath) {
|
|
160
|
+
let raw;
|
|
161
|
+
try {
|
|
162
|
+
raw = await fs.readFile(configPath, "utf-8");
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const code = err.code;
|
|
165
|
+
if (code === "ENOENT") {
|
|
166
|
+
throw new Error(`Config file not found: ${configPath}. Run "npx viberails" to generate one.`);
|
|
167
|
+
}
|
|
168
|
+
throw new Error(`Failed to read config file at ${configPath}: ${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(raw);
|
|
173
|
+
} catch {
|
|
174
|
+
throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);
|
|
175
|
+
}
|
|
176
|
+
validateConfig(parsed, configPath);
|
|
177
|
+
return parsed;
|
|
178
|
+
}
|
|
179
|
+
async function loadConfigSafe(configPath) {
|
|
180
|
+
try {
|
|
181
|
+
return await loadConfig(configPath);
|
|
182
|
+
} catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/merge-config.ts
|
|
188
|
+
function mergeStack(existing, fresh) {
|
|
189
|
+
return {
|
|
190
|
+
language: existing.language,
|
|
191
|
+
packageManager: existing.packageManager,
|
|
192
|
+
framework: existing.framework ?? fresh.framework,
|
|
193
|
+
styling: existing.styling ?? fresh.styling,
|
|
194
|
+
backend: existing.backend ?? fresh.backend,
|
|
195
|
+
linter: existing.linter ?? fresh.linter,
|
|
196
|
+
testRunner: existing.testRunner ?? fresh.testRunner
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function mergeStructure(existing, fresh) {
|
|
200
|
+
return {
|
|
201
|
+
srcDir: existing.srcDir ?? fresh.srcDir,
|
|
202
|
+
pages: existing.pages ?? fresh.pages,
|
|
203
|
+
components: existing.components ?? fresh.components,
|
|
204
|
+
hooks: existing.hooks ?? fresh.hooks,
|
|
205
|
+
utils: existing.utils ?? fresh.utils,
|
|
206
|
+
types: existing.types ?? fresh.types,
|
|
207
|
+
tests: existing.tests ?? fresh.tests,
|
|
208
|
+
testPattern: existing.testPattern ?? fresh.testPattern
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function hasConvention(conventions, key) {
|
|
212
|
+
return conventions[key] !== void 0;
|
|
213
|
+
}
|
|
214
|
+
function markAsDetected(value) {
|
|
215
|
+
if (typeof value === "string") {
|
|
216
|
+
return { value, _confidence: "high", _consistency: 100, _detected: true };
|
|
217
|
+
}
|
|
218
|
+
return { ...value, _detected: true };
|
|
219
|
+
}
|
|
220
|
+
var CONVENTION_KEYS2 = [
|
|
221
|
+
"fileNaming",
|
|
222
|
+
"componentNaming",
|
|
223
|
+
"hookNaming",
|
|
224
|
+
"importAlias"
|
|
225
|
+
];
|
|
226
|
+
function mergeConventions(existing, fresh) {
|
|
227
|
+
const merged = { ...existing };
|
|
228
|
+
for (const key of CONVENTION_KEYS2) {
|
|
229
|
+
if (!hasConvention(existing, key) && fresh[key] !== void 0) {
|
|
230
|
+
merged[key] = markAsDetected(fresh[key]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return merged;
|
|
234
|
+
}
|
|
235
|
+
function mergeConfig(existing, scanResult) {
|
|
236
|
+
const fresh = generateConfig(scanResult);
|
|
237
|
+
const merged = {
|
|
238
|
+
$schema: existing.$schema ?? fresh.$schema,
|
|
239
|
+
version: existing.version,
|
|
240
|
+
name: existing.name,
|
|
241
|
+
enforcement: existing.enforcement,
|
|
242
|
+
stack: mergeStack(existing.stack, fresh.stack),
|
|
243
|
+
structure: mergeStructure(existing.structure, fresh.structure),
|
|
244
|
+
conventions: mergeConventions(existing.conventions, fresh.conventions),
|
|
245
|
+
rules: { ...existing.rules },
|
|
246
|
+
ignore: [...existing.ignore]
|
|
247
|
+
};
|
|
248
|
+
if (fresh.workspace) {
|
|
249
|
+
merged.workspace = fresh.workspace;
|
|
250
|
+
}
|
|
251
|
+
if (existing.boundaries) {
|
|
252
|
+
merged.boundaries = [...existing.boundaries];
|
|
253
|
+
} else if (fresh.boundaries) {
|
|
254
|
+
merged.boundaries = [...fresh.boundaries];
|
|
255
|
+
}
|
|
256
|
+
return merged;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/schema.ts
|
|
260
|
+
var configSchema = {
|
|
261
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
262
|
+
$id: "https://viberails.sh/schema/v1.json",
|
|
263
|
+
title: "viberails configuration",
|
|
264
|
+
description: "Configuration file for viberails \u2014 guardrails for vibe coding.",
|
|
265
|
+
type: "object",
|
|
266
|
+
required: ["version", "name", "stack", "rules"],
|
|
267
|
+
properties: {
|
|
268
|
+
$schema: {
|
|
269
|
+
type: "string",
|
|
270
|
+
description: "JSON Schema URL for editor validation."
|
|
271
|
+
},
|
|
272
|
+
version: {
|
|
273
|
+
type: "number",
|
|
274
|
+
const: 1,
|
|
275
|
+
description: "Config format version. Always 1 for V1.0."
|
|
276
|
+
},
|
|
277
|
+
name: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Project name, typically from package.json."
|
|
280
|
+
},
|
|
281
|
+
enforcement: {
|
|
282
|
+
type: "string",
|
|
283
|
+
enum: ["warn", "enforce"],
|
|
284
|
+
default: "warn",
|
|
285
|
+
description: "Whether conventions are warned about or enforced as errors."
|
|
286
|
+
},
|
|
287
|
+
stack: {
|
|
288
|
+
type: "object",
|
|
289
|
+
required: ["language", "packageManager"],
|
|
290
|
+
properties: {
|
|
291
|
+
framework: {
|
|
292
|
+
type: "string",
|
|
293
|
+
description: 'Primary framework identifier (e.g. "nextjs@15", "remix@2").'
|
|
294
|
+
},
|
|
295
|
+
language: {
|
|
296
|
+
type: "string",
|
|
297
|
+
description: 'Primary language (e.g. "typescript", "javascript").'
|
|
298
|
+
},
|
|
299
|
+
styling: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: 'Styling solution (e.g. "tailwindcss@4", "css-modules").'
|
|
302
|
+
},
|
|
303
|
+
backend: {
|
|
304
|
+
type: "string",
|
|
305
|
+
description: 'Backend framework (e.g. "express@5", "fastify").'
|
|
306
|
+
},
|
|
307
|
+
packageManager: {
|
|
308
|
+
type: "string",
|
|
309
|
+
description: 'Package manager (e.g. "pnpm", "npm", "yarn").'
|
|
310
|
+
},
|
|
311
|
+
linter: {
|
|
312
|
+
type: "string",
|
|
313
|
+
description: 'Linter (e.g. "eslint@9", "biome").'
|
|
314
|
+
},
|
|
315
|
+
testRunner: {
|
|
316
|
+
type: "string",
|
|
317
|
+
description: 'Test runner (e.g. "vitest", "jest").'
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
additionalProperties: false,
|
|
321
|
+
description: "Detected or configured technology stack."
|
|
322
|
+
},
|
|
323
|
+
structure: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
srcDir: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: 'Source directory (e.g. "src"), or omit for flat structure.'
|
|
329
|
+
},
|
|
330
|
+
pages: {
|
|
331
|
+
type: "string",
|
|
332
|
+
description: 'Pages or routes directory (e.g. "src/app").'
|
|
333
|
+
},
|
|
334
|
+
components: {
|
|
335
|
+
type: "string",
|
|
336
|
+
description: 'Components directory (e.g. "src/components").'
|
|
337
|
+
},
|
|
338
|
+
hooks: {
|
|
339
|
+
type: "string",
|
|
340
|
+
description: 'Hooks directory (e.g. "src/hooks").'
|
|
341
|
+
},
|
|
342
|
+
utils: {
|
|
343
|
+
type: "string",
|
|
344
|
+
description: 'Utilities directory (e.g. "src/utils", "src/lib").'
|
|
345
|
+
},
|
|
346
|
+
types: {
|
|
347
|
+
type: "string",
|
|
348
|
+
description: 'Type definitions directory (e.g. "src/types").'
|
|
349
|
+
},
|
|
350
|
+
tests: {
|
|
351
|
+
type: "string",
|
|
352
|
+
description: 'Tests directory (e.g. "tests", "__tests__").'
|
|
353
|
+
},
|
|
354
|
+
testPattern: {
|
|
355
|
+
type: "string",
|
|
356
|
+
description: 'Test file naming pattern (e.g. "*.test.ts", "*.spec.ts").'
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
additionalProperties: false,
|
|
360
|
+
description: "Detected or configured directory structure."
|
|
361
|
+
},
|
|
362
|
+
conventions: {
|
|
363
|
+
type: "object",
|
|
364
|
+
properties: {
|
|
365
|
+
fileNaming: { $ref: "#/definitions/conventionValue" },
|
|
366
|
+
componentNaming: { $ref: "#/definitions/conventionValue" },
|
|
367
|
+
hookNaming: { $ref: "#/definitions/conventionValue" },
|
|
368
|
+
importAlias: { $ref: "#/definitions/conventionValue" }
|
|
369
|
+
},
|
|
370
|
+
additionalProperties: false,
|
|
371
|
+
description: "Detected or configured coding conventions."
|
|
372
|
+
},
|
|
373
|
+
rules: {
|
|
374
|
+
type: "object",
|
|
375
|
+
required: [
|
|
376
|
+
"maxFileLines",
|
|
377
|
+
"maxFunctionLines",
|
|
378
|
+
"requireTests",
|
|
379
|
+
"enforceNaming",
|
|
380
|
+
"enforceBoundaries"
|
|
381
|
+
],
|
|
382
|
+
properties: {
|
|
383
|
+
maxFileLines: {
|
|
384
|
+
type: "number",
|
|
385
|
+
default: 300,
|
|
386
|
+
description: "Maximum number of lines allowed per file."
|
|
387
|
+
},
|
|
388
|
+
maxFunctionLines: {
|
|
389
|
+
type: "number",
|
|
390
|
+
default: 50,
|
|
391
|
+
description: "Maximum number of lines allowed per function."
|
|
392
|
+
},
|
|
393
|
+
requireTests: {
|
|
394
|
+
type: "boolean",
|
|
395
|
+
default: true,
|
|
396
|
+
description: "Whether to require test files for source modules."
|
|
397
|
+
},
|
|
398
|
+
enforceNaming: {
|
|
399
|
+
type: "boolean",
|
|
400
|
+
default: true,
|
|
401
|
+
description: "Whether to enforce detected file naming conventions."
|
|
402
|
+
},
|
|
403
|
+
enforceBoundaries: {
|
|
404
|
+
type: "boolean",
|
|
405
|
+
default: false,
|
|
406
|
+
description: "Whether to enforce module boundary rules."
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
additionalProperties: false,
|
|
410
|
+
description: "Rule thresholds and toggles for enforcement."
|
|
411
|
+
},
|
|
412
|
+
ignore: {
|
|
413
|
+
type: "array",
|
|
414
|
+
items: { type: "string" },
|
|
415
|
+
description: "Glob patterns for files and directories to ignore."
|
|
416
|
+
},
|
|
417
|
+
boundaries: {
|
|
418
|
+
type: "array",
|
|
419
|
+
items: {
|
|
420
|
+
type: "object",
|
|
421
|
+
required: ["from", "to", "allow"],
|
|
422
|
+
properties: {
|
|
423
|
+
from: {
|
|
424
|
+
type: "string",
|
|
425
|
+
description: "Source package or directory pattern."
|
|
426
|
+
},
|
|
427
|
+
to: {
|
|
428
|
+
type: "string",
|
|
429
|
+
description: "Target package or directory pattern."
|
|
430
|
+
},
|
|
431
|
+
allow: {
|
|
432
|
+
type: "boolean",
|
|
433
|
+
description: "Whether this import direction is allowed or disallowed."
|
|
434
|
+
},
|
|
435
|
+
reason: {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "Human-readable explanation of why this boundary exists."
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
additionalProperties: false
|
|
441
|
+
},
|
|
442
|
+
description: "Module boundary rules for import enforcement."
|
|
443
|
+
},
|
|
444
|
+
workspace: {
|
|
445
|
+
type: "object",
|
|
446
|
+
required: ["packages", "isMonorepo"],
|
|
447
|
+
properties: {
|
|
448
|
+
packages: {
|
|
449
|
+
type: "array",
|
|
450
|
+
items: { type: "string" },
|
|
451
|
+
description: "Relative paths to workspace packages."
|
|
452
|
+
},
|
|
453
|
+
isMonorepo: {
|
|
454
|
+
type: "boolean",
|
|
455
|
+
description: "Whether this project is a monorepo with multiple packages."
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
additionalProperties: false,
|
|
459
|
+
description: "Workspace configuration for monorepo projects."
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
additionalProperties: false,
|
|
463
|
+
definitions: {
|
|
464
|
+
conventionValue: {
|
|
465
|
+
description: "A convention value \u2014 either a plain string (user-confirmed) or an object with scanner metadata.",
|
|
466
|
+
oneOf: [
|
|
467
|
+
{ type: "string" },
|
|
468
|
+
{
|
|
469
|
+
type: "object",
|
|
470
|
+
required: ["value", "_confidence", "_consistency"],
|
|
471
|
+
properties: {
|
|
472
|
+
value: {
|
|
473
|
+
type: "string",
|
|
474
|
+
description: "The convention value."
|
|
475
|
+
},
|
|
476
|
+
_confidence: {
|
|
477
|
+
type: "string",
|
|
478
|
+
enum: ["high", "medium", "low"],
|
|
479
|
+
description: "Scanner confidence level."
|
|
480
|
+
},
|
|
481
|
+
_consistency: {
|
|
482
|
+
type: "number",
|
|
483
|
+
minimum: 0,
|
|
484
|
+
maximum: 100,
|
|
485
|
+
description: "Scanner consistency percentage."
|
|
486
|
+
},
|
|
487
|
+
_detected: {
|
|
488
|
+
type: "boolean",
|
|
489
|
+
description: "Set by mergeConfig when a convention is newly detected during sync."
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
additionalProperties: false
|
|
493
|
+
}
|
|
494
|
+
]
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/index.ts
|
|
500
|
+
var VERSION = "0.1.0";
|
|
501
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
502
|
+
0 && (module.exports = {
|
|
503
|
+
DEFAULT_IGNORE,
|
|
504
|
+
DEFAULT_RULES,
|
|
505
|
+
VERSION,
|
|
506
|
+
configSchema,
|
|
507
|
+
generateConfig,
|
|
508
|
+
loadConfig,
|
|
509
|
+
loadConfigSafe,
|
|
510
|
+
mergeConfig
|
|
511
|
+
});
|
|
512
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/defaults.ts","../src/generate-config.ts","../src/load-config.ts","../src/merge-config.ts","../src/schema.ts"],"sourcesContent":["export const VERSION = '0.1.0';\n\nexport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nexport { generateConfig } from './generate-config.js';\nexport { loadConfig, loadConfigSafe } from './load-config.js';\nexport { mergeConfig } from './merge-config.js';\nexport { configSchema } from './schema.js';\n","import type { ConfigRules } from '@viberails/types';\n\n/**\n * Default rule thresholds and toggles for a new viberails config.\n * These values are intentionally conservative to build trust on first run.\n */\nexport const DEFAULT_RULES: ConfigRules = {\n maxFileLines: 300,\n maxFunctionLines: 50,\n requireTests: true,\n enforceNaming: true,\n enforceBoundaries: false,\n};\n\n/**\n * Default glob patterns for files and directories to ignore.\n */\nexport const DEFAULT_IGNORE: string[] = ['**/*.d.ts', 'dist/**', 'node_modules/**'];\n","import * as path from 'node:path';\nimport type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n DetectedConvention,\n DirectoryRole,\n ScanResult,\n StackItem,\n ViberailsConfig,\n} from '@viberails/types';\nimport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\n\n/**\n * Format a StackItem as a config string: `\"name@version\"` or `\"name\"`.\n */\nfunction formatStackItem(item: StackItem): string {\n return item.version ? `${item.name}@${item.version}` : item.name;\n}\n\n/**\n * Map DetectedStack → ConfigStack by formatting each StackItem.\n */\nfunction mapStack(scanResult: ScanResult): ConfigStack {\n const { stack } = scanResult;\n const config: ConfigStack = {\n language: formatStackItem(stack.language),\n packageManager: formatStackItem(stack.packageManager),\n };\n\n if (stack.framework) config.framework = formatStackItem(stack.framework);\n if (stack.styling) config.styling = formatStackItem(stack.styling);\n if (stack.backend) config.backend = formatStackItem(stack.backend);\n if (stack.linter) config.linter = formatStackItem(stack.linter);\n if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);\n\n return config;\n}\n\n/** Directory roles that map to ConfigStructure fields. */\nconst ROLE_TO_FIELD: Partial<Record<DirectoryRole, keyof ConfigStructure>> = {\n pages: 'pages',\n components: 'components',\n hooks: 'hooks',\n utils: 'utils',\n types: 'types',\n tests: 'tests',\n};\n\n/**\n * Map DetectedStructure → ConfigStructure by finding the first directory\n * for each known role.\n */\nfunction mapStructure(scanResult: ScanResult): ConfigStructure {\n const { structure } = scanResult;\n const config: ConfigStructure = {};\n\n if (structure.srcDir) {\n config.srcDir = structure.srcDir;\n }\n\n for (const dir of structure.directories) {\n const field = ROLE_TO_FIELD[dir.role];\n if (field && config[field] === undefined) {\n (config as Record<string, string>)[field] = dir.path;\n }\n }\n\n if (structure.testPattern) {\n config.testPattern = structure.testPattern.value;\n }\n\n return config;\n}\n\n/**\n * Convert a DetectedConvention to a ConventionValue with metadata.\n * Returns undefined for low-confidence conventions (they are omitted).\n */\nfunction mapConvention(convention: DetectedConvention): ConventionValue | undefined {\n if (convention.confidence === 'low') {\n return undefined;\n }\n\n return {\n value: convention.value,\n _confidence: convention.confidence,\n _consistency: convention.consistency,\n };\n}\n\n/** Convention keys from ScanResult that map to ConfigConventions fields. */\nconst CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Map scanner conventions → ConfigConventions, omitting low-confidence entries.\n */\nfunction mapConventions(scanResult: ScanResult): ConfigConventions {\n const config: ConfigConventions = {};\n\n for (const key of CONVENTION_KEYS) {\n const detected = scanResult.conventions[key];\n if (detected) {\n const value = mapConvention(detected);\n if (value !== undefined) {\n config[key] = value;\n }\n }\n }\n\n return config;\n}\n\n/**\n * Generate a ViberailsConfig from scan results.\n *\n * Maps the scanner's DetectedStack, DetectedStructure, and conventions\n * into the config format with smart defaults. Low-confidence conventions\n * are omitted. The project name is derived from the root directory basename.\n *\n * @param scanResult - The output of scanning a project\n * @returns A complete ViberailsConfig ready to be written as JSON\n */\nexport function generateConfig(scanResult: ScanResult): ViberailsConfig {\n const config: ViberailsConfig = {\n $schema: 'https://viberails.sh/schema/v1.json',\n version: 1,\n name: path.basename(scanResult.root),\n enforcement: 'warn',\n stack: mapStack(scanResult),\n structure: mapStructure(scanResult),\n conventions: mapConventions(scanResult),\n rules: { ...DEFAULT_RULES },\n ignore: [...DEFAULT_IGNORE],\n };\n\n if (scanResult.workspace) {\n config.workspace = {\n packages: scanResult.workspace.packages.map((p) => p.relativePath),\n isMonorepo: true,\n };\n config.boundaries = [];\n }\n\n return config;\n}\n","import * as fs from 'node:fs/promises';\nimport type { ViberailsConfig } from '@viberails/types';\n\n/**\n * Validate that a parsed object has the required ViberailsConfig fields.\n * Throws a descriptive error if any required field is missing.\n */\nfunction validateConfig(parsed: Record<string, unknown>, configPath: string): void {\n const required = ['version', 'name', 'stack', 'rules'] as const;\n const missing = required.filter((field) => parsed[field] === undefined);\n\n if (missing.length > 0) {\n throw new Error(\n `Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(', ')}`,\n );\n }\n}\n\n/**\n * Load and parse a viberails config file.\n *\n * Reads the JSON file at the given path, validates that it contains\n * the required fields (version, name, stack, rules), and returns\n * the parsed config.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig\n * @throws If the file doesn't exist, contains invalid JSON, or is missing required fields\n */\nexport async function loadConfig(configPath: string): Promise<ViberailsConfig> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}. Run \"npx viberails\" to generate one.`);\n }\n throw new Error(`Failed to read config file at ${configPath}: ${(err as Error).message}`);\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);\n }\n\n validateConfig(parsed, configPath);\n\n return parsed as unknown as ViberailsConfig;\n}\n\n/**\n * Safely load a viberails config file, returning null on any error.\n *\n * Used by the CLI for \"does config already exist?\" checks where\n * failure is an expected case, not an error.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig, or null if loading fails\n */\nexport async function loadConfigSafe(configPath: string): Promise<ViberailsConfig | null> {\n try {\n return await loadConfig(configPath);\n } catch {\n return null;\n }\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { generateConfig } from './generate-config.js';\n\n/**\n * Merge stack: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStack(existing: ConfigStack, fresh: ConfigStack): ConfigStack {\n return {\n language: existing.language,\n packageManager: existing.packageManager,\n framework: existing.framework ?? fresh.framework,\n styling: existing.styling ?? fresh.styling,\n backend: existing.backend ?? fresh.backend,\n linter: existing.linter ?? fresh.linter,\n testRunner: existing.testRunner ?? fresh.testRunner,\n };\n}\n\n/**\n * Merge structure: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStructure(existing: ConfigStructure, fresh: ConfigStructure): ConfigStructure {\n return {\n srcDir: existing.srcDir ?? fresh.srcDir,\n pages: existing.pages ?? fresh.pages,\n components: existing.components ?? fresh.components,\n hooks: existing.hooks ?? fresh.hooks,\n utils: existing.utils ?? fresh.utils,\n types: existing.types ?? fresh.types,\n tests: existing.tests ?? fresh.tests,\n testPattern: existing.testPattern ?? fresh.testPattern,\n };\n}\n\n/**\n * Check if a convention key exists in the existing config\n * (either as a string or as an object with a value).\n */\nfunction hasConvention(conventions: ConfigConventions, key: keyof ConfigConventions): boolean {\n return conventions[key] !== undefined;\n}\n\n/**\n * Mark a ConventionValue as newly detected by adding `_detected: true`.\n * Only applies to object-form values (not plain strings).\n */\nfunction markAsDetected(value: ConventionValue): ConventionValue {\n if (typeof value === 'string') {\n return { value, _confidence: 'high', _consistency: 100, _detected: true };\n }\n return { ...value, _detected: true };\n}\n\n/** Convention keys to iterate during merge. */\nconst CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Merge conventions: keep all existing values, add new detections with `_detected: true`.\n */\nfunction mergeConventions(\n existing: ConfigConventions,\n fresh: ConfigConventions,\n): ConfigConventions {\n const merged: ConfigConventions = { ...existing };\n\n for (const key of CONVENTION_KEYS) {\n if (!hasConvention(existing, key) && fresh[key] !== undefined) {\n merged[key] = markAsDetected(fresh[key]!);\n }\n }\n\n return merged;\n}\n\n/**\n * Merge a new scan result into an existing config for `viberails sync`.\n *\n * Preserves all developer-confirmed values from the existing config.\n * Adds newly detected conventions with a `_detected: true` annotation\n * so the developer can review them. Never removes rules or values\n * the developer has set.\n *\n * @param existing - The current ViberailsConfig (from viberails.config.json)\n * @param scanResult - Fresh scan results from re-scanning the project\n * @returns A merged config that preserves existing values and adds new detections\n */\nexport function mergeConfig(existing: ViberailsConfig, scanResult: ScanResult): ViberailsConfig {\n const fresh = generateConfig(scanResult);\n\n const merged: ViberailsConfig = {\n $schema: existing.$schema ?? fresh.$schema,\n version: existing.version,\n name: existing.name,\n enforcement: existing.enforcement,\n stack: mergeStack(existing.stack, fresh.stack),\n structure: mergeStructure(existing.structure, fresh.structure),\n conventions: mergeConventions(existing.conventions, fresh.conventions),\n rules: { ...existing.rules },\n ignore: [...existing.ignore],\n };\n\n // Workspace: always take fresh scan (structure can change)\n if (fresh.workspace) {\n merged.workspace = fresh.workspace;\n }\n\n // Boundaries: preserve existing rules (user may have adjusted)\n if (existing.boundaries) {\n merged.boundaries = [...existing.boundaries];\n } else if (fresh.boundaries) {\n merged.boundaries = [...fresh.boundaries];\n }\n\n return merged;\n}\n","/**\n * JSON Schema (draft-07) definition for viberails.config.json.\n *\n * This schema will eventually be hosted at https://viberails.sh/schema/v1.json.\n * For now it is exported as a TypeScript object that can be serialized to JSON.\n */\nexport const configSchema = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://viberails.sh/schema/v1.json',\n title: 'viberails configuration',\n description: 'Configuration file for viberails — guardrails for vibe coding.',\n type: 'object',\n required: ['version', 'name', 'stack', 'rules'],\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema URL for editor validation.',\n },\n version: {\n type: 'number',\n const: 1,\n description: 'Config format version. Always 1 for V1.0.',\n },\n name: {\n type: 'string',\n description: 'Project name, typically from package.json.',\n },\n enforcement: {\n type: 'string',\n enum: ['warn', 'enforce'],\n default: 'warn',\n description: 'Whether conventions are warned about or enforced as errors.',\n },\n stack: {\n type: 'object',\n required: ['language', 'packageManager'],\n properties: {\n framework: {\n type: 'string',\n description: 'Primary framework identifier (e.g. \"nextjs@15\", \"remix@2\").',\n },\n language: {\n type: 'string',\n description: 'Primary language (e.g. \"typescript\", \"javascript\").',\n },\n styling: {\n type: 'string',\n description: 'Styling solution (e.g. \"tailwindcss@4\", \"css-modules\").',\n },\n backend: {\n type: 'string',\n description: 'Backend framework (e.g. \"express@5\", \"fastify\").',\n },\n packageManager: {\n type: 'string',\n description: 'Package manager (e.g. \"pnpm\", \"npm\", \"yarn\").',\n },\n linter: {\n type: 'string',\n description: 'Linter (e.g. \"eslint@9\", \"biome\").',\n },\n testRunner: {\n type: 'string',\n description: 'Test runner (e.g. \"vitest\", \"jest\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured technology stack.',\n },\n structure: {\n type: 'object',\n properties: {\n srcDir: {\n type: 'string',\n description: 'Source directory (e.g. \"src\"), or omit for flat structure.',\n },\n pages: {\n type: 'string',\n description: 'Pages or routes directory (e.g. \"src/app\").',\n },\n components: {\n type: 'string',\n description: 'Components directory (e.g. \"src/components\").',\n },\n hooks: {\n type: 'string',\n description: 'Hooks directory (e.g. \"src/hooks\").',\n },\n utils: {\n type: 'string',\n description: 'Utilities directory (e.g. \"src/utils\", \"src/lib\").',\n },\n types: {\n type: 'string',\n description: 'Type definitions directory (e.g. \"src/types\").',\n },\n tests: {\n type: 'string',\n description: 'Tests directory (e.g. \"tests\", \"__tests__\").',\n },\n testPattern: {\n type: 'string',\n description: 'Test file naming pattern (e.g. \"*.test.ts\", \"*.spec.ts\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured directory structure.',\n },\n conventions: {\n type: 'object',\n properties: {\n fileNaming: { $ref: '#/definitions/conventionValue' },\n componentNaming: { $ref: '#/definitions/conventionValue' },\n hookNaming: { $ref: '#/definitions/conventionValue' },\n importAlias: { $ref: '#/definitions/conventionValue' },\n },\n additionalProperties: false,\n description: 'Detected or configured coding conventions.',\n },\n rules: {\n type: 'object',\n required: [\n 'maxFileLines',\n 'maxFunctionLines',\n 'requireTests',\n 'enforceNaming',\n 'enforceBoundaries',\n ],\n properties: {\n maxFileLines: {\n type: 'number',\n default: 300,\n description: 'Maximum number of lines allowed per file.',\n },\n maxFunctionLines: {\n type: 'number',\n default: 50,\n description: 'Maximum number of lines allowed per function.',\n },\n requireTests: {\n type: 'boolean',\n default: true,\n description: 'Whether to require test files for source modules.',\n },\n enforceNaming: {\n type: 'boolean',\n default: true,\n description: 'Whether to enforce detected file naming conventions.',\n },\n enforceBoundaries: {\n type: 'boolean',\n default: false,\n description: 'Whether to enforce module boundary rules.',\n },\n },\n additionalProperties: false,\n description: 'Rule thresholds and toggles for enforcement.',\n },\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description: 'Glob patterns for files and directories to ignore.',\n },\n boundaries: {\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to', 'allow'],\n properties: {\n from: {\n type: 'string',\n description: 'Source package or directory pattern.',\n },\n to: {\n type: 'string',\n description: 'Target package or directory pattern.',\n },\n allow: {\n type: 'boolean',\n description: 'Whether this import direction is allowed or disallowed.',\n },\n reason: {\n type: 'string',\n description: 'Human-readable explanation of why this boundary exists.',\n },\n },\n additionalProperties: false,\n },\n description: 'Module boundary rules for import enforcement.',\n },\n workspace: {\n type: 'object',\n required: ['packages', 'isMonorepo'],\n properties: {\n packages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Relative paths to workspace packages.',\n },\n isMonorepo: {\n type: 'boolean',\n description: 'Whether this project is a monorepo with multiple packages.',\n },\n },\n additionalProperties: false,\n description: 'Workspace configuration for monorepo projects.',\n },\n },\n additionalProperties: false,\n definitions: {\n conventionValue: {\n description:\n 'A convention value — either a plain string (user-confirmed) or an object with scanner metadata.',\n oneOf: [\n { type: 'string' },\n {\n type: 'object',\n required: ['value', '_confidence', '_consistency'],\n properties: {\n value: {\n type: 'string',\n description: 'The convention value.',\n },\n _confidence: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Scanner confidence level.',\n },\n _consistency: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Scanner consistency percentage.',\n },\n _detected: {\n type: 'boolean',\n description: 'Set by mergeConfig when a convention is newly detected during sync.',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n },\n} as const;\n"],"mappings":"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","names":["CONVENTION_KEYS"]}
|