@kabran-tecnologia/kabran-config 1.12.0 → 2.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kabran-tecnologia/kabran-config",
3
- "version": "1.12.0",
3
+ "version": "2.0.0",
4
4
  "description": "Shared quality configurations, enforcement scripts, and CI/CD tooling for Kabran projects",
5
5
  "author": "Kabran",
6
6
  "license": "MIT",
@@ -81,6 +81,9 @@ export async function runTest(level, config, args) {
81
81
 
82
82
  console.log('\n' + '='.repeat(50));
83
83
  console.log(`Running L${levelNumber(level)} tests: ${level}`);
84
+ if (testConfig.doppler) {
85
+ console.log('[Doppler] Secrets injection enabled');
86
+ }
84
87
  console.log('='.repeat(50));
85
88
 
86
89
  // Setup phase (if defined)
@@ -4,13 +4,19 @@
4
4
  * Loads project configuration from kabran.config.mjs/js/json with fallback to defaults.
5
5
  * Enables projects to customize validator behavior without modifying kabran-config.
6
6
  *
7
+ * Features:
8
+ * - Smart detection of tools (ESLint, TypeScript, Prettier, Vitest, Playwright)
9
+ * - Automatic Doppler integration for secrets injection in tests
10
+ * - Convention over configuration - works out of the box
11
+ *
7
12
  * @module config-loader
8
13
  */
9
14
 
10
- import {existsSync} from 'node:fs';
15
+ import {existsSync, readFileSync} from 'node:fs';
11
16
  import {readFile} from 'node:fs/promises';
12
17
  import {join} from 'node:path';
13
18
  import {pathToFileURL} from 'node:url';
19
+ import {execSync} from 'node:child_process';
14
20
 
15
21
  /**
16
22
  * Default configuration values.
@@ -30,6 +36,140 @@ export const DEFAULTS = {
30
36
  },
31
37
  };
32
38
 
39
+ /**
40
+ * Config file patterns for each tool.
41
+ */
42
+ const TOOL_CONFIGS = {
43
+ eslint: ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs', '.eslintrc.js', '.eslintrc.json', '.eslintrc.cjs'],
44
+ typescript: ['tsconfig.json'],
45
+ prettier: ['prettier.config.mjs', 'prettier.config.js', '.prettierrc', '.prettierrc.json', '.prettierrc.js'],
46
+ vitest: ['vitest.config.ts', 'vitest.config.mts', 'vitest.config.js', 'vitest.config.mjs'],
47
+ playwright: ['playwright.config.ts', 'playwright.config.js'],
48
+ };
49
+
50
+ /**
51
+ * Check if any of the config files exist for a tool.
52
+ * @param {string} cwd - Working directory
53
+ * @param {string[]} configFiles - List of possible config file names
54
+ * @returns {boolean} True if any config file exists
55
+ */
56
+ function hasToolConfig(cwd, configFiles) {
57
+ return configFiles.some(file => existsSync(join(cwd, file)));
58
+ }
59
+
60
+ /**
61
+ * Check if Doppler is configured for the current directory.
62
+ * @param {string} cwd - Working directory
63
+ * @returns {boolean} True if Doppler token is configured
64
+ */
65
+ function hasDopplerConfigured(cwd) {
66
+ try {
67
+ const result = execSync('doppler configure get token', {
68
+ cwd,
69
+ stdio: 'pipe',
70
+ encoding: 'utf-8',
71
+ timeout: 5000,
72
+ });
73
+ return result.trim().length > 0;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Wrap command with Doppler if configured.
81
+ * @param {string} cmd - Command to wrap
82
+ * @param {boolean} useDoppler - Whether to use Doppler
83
+ * @returns {string} Wrapped command
84
+ */
85
+ export function wrapWithDoppler(cmd, useDoppler) {
86
+ return useDoppler ? `doppler run -- ${cmd}` : cmd;
87
+ }
88
+
89
+ /**
90
+ * Detect available tools and generate smart defaults.
91
+ * Only configures commands for tools that are actually set up in the project.
92
+ * @param {string} cwd - Working directory
93
+ * @returns {object} Detected defaults for CLI commands
94
+ */
95
+ export function detectToolDefaults(cwd) {
96
+ const defaults = {
97
+ check: {},
98
+ test: {},
99
+ build: {},
100
+ ci: { steps: [] },
101
+ };
102
+
103
+ const hasDoppler = hasDopplerConfigured(cwd);
104
+
105
+ // L1: Static Analysis
106
+ if (hasToolConfig(cwd, TOOL_CONFIGS.eslint)) {
107
+ defaults.check.lint = 'npx eslint .';
108
+ }
109
+
110
+ if (hasToolConfig(cwd, TOOL_CONFIGS.typescript)) {
111
+ defaults.check.types = 'npx tsc --noEmit';
112
+ }
113
+
114
+ if (hasToolConfig(cwd, TOOL_CONFIGS.prettier)) {
115
+ defaults.check.format = 'npx prettier --check .';
116
+ }
117
+
118
+ // L2: Unit Tests (with Doppler support)
119
+ if (hasToolConfig(cwd, TOOL_CONFIGS.vitest)) {
120
+ const cmd = 'npx vitest run';
121
+ defaults.test.unit = {
122
+ command: wrapWithDoppler(cmd, hasDoppler),
123
+ doppler: hasDoppler,
124
+ };
125
+ }
126
+
127
+ // L3: Integration Tests (with Doppler support)
128
+ const integrationConfig = join(cwd, 'vitest.integration.config.ts');
129
+ if (existsSync(integrationConfig)) {
130
+ const cmd = 'npx vitest run --config vitest.integration.config.ts';
131
+ defaults.test.integration = {
132
+ command: wrapWithDoppler(cmd, hasDoppler),
133
+ doppler: hasDoppler,
134
+ };
135
+ }
136
+
137
+ // L4: E2E Tests (with Doppler support)
138
+ if (hasToolConfig(cwd, TOOL_CONFIGS.playwright)) {
139
+ const cmd = 'npx playwright test';
140
+ defaults.test.e2e = {
141
+ command: wrapWithDoppler(cmd, hasDoppler),
142
+ doppler: hasDoppler,
143
+ };
144
+ }
145
+
146
+ // Build - check if npm run build exists in package.json
147
+ const packageJsonPath = join(cwd, 'package.json');
148
+ if (existsSync(packageJsonPath)) {
149
+ try {
150
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
151
+ if (packageJson.scripts?.build) {
152
+ defaults.build.command = 'npm run build';
153
+ }
154
+ } catch {
155
+ // Ignore parse errors
156
+ }
157
+ }
158
+
159
+ // CI steps - add detected tools
160
+ if (Object.keys(defaults.check).length > 0) {
161
+ defaults.ci.steps.push('check');
162
+ }
163
+ if (defaults.test.unit) {
164
+ defaults.ci.steps.push('test:unit');
165
+ }
166
+ if (defaults.build.command) {
167
+ defaults.ci.steps.push('build');
168
+ }
169
+
170
+ return defaults;
171
+ }
172
+
33
173
  /**
34
174
  * Configuration file names to search for, in priority order.
35
175
  */
@@ -91,27 +231,38 @@ async function loadJsConfig(configPath) {
91
231
  /**
92
232
  * Load project configuration with fallback to defaults.
93
233
  *
94
- * Searches for configuration files in order:
95
- * 1. kabran.config.mjs
96
- * 2. kabran.config.js
97
- * 3. kabran.config.json
234
+ * Configuration priority (highest to lowest):
235
+ * 1. Project config (kabran.config.mjs/js/json)
236
+ * 2. Detected tool defaults (based on existing config files)
237
+ * 3. Static defaults (validators)
98
238
  *
99
- * If no config file is found, returns DEFAULTS.
239
+ * Features:
240
+ * - Smart detection of ESLint, TypeScript, Prettier, Vitest, Playwright
241
+ * - Automatic Doppler integration for secrets in tests
242
+ * - Project-specific overrides take precedence
100
243
  *
101
244
  * @param {string} [cwd=process.cwd()] - Directory to search for config
102
245
  * @returns {Promise<object>} Merged configuration
103
246
  *
104
247
  * @example
248
+ * // Zero-config: auto-detects tools in project
105
249
  * const config = await loadConfig();
106
- * console.log(config.readme.required); // ['Installation', 'Usage', 'License']
250
+ * // If project has vitest.config.ts, config.test.unit.command is set
107
251
  *
108
252
  * @example
109
- * // With custom config in kabran.config.mjs:
110
- * // export default { readme: { required: ['Setup', 'API'] } }
253
+ * // With custom config in kabran.config.mjs (overrides detected defaults):
254
+ * // export default { test: { unit: { command: 'npm test' } } }
111
255
  * const config = await loadConfig('/path/to/project');
112
- * console.log(config.readme.required); // ['Setup', 'API']
113
256
  */
114
257
  export async function loadConfig(cwd = process.cwd()) {
258
+ // 1. Start with static defaults (validators)
259
+ let config = {...DEFAULTS};
260
+
261
+ // 2. Merge detected tool defaults (CLI commands)
262
+ const toolDefaults = detectToolDefaults(cwd);
263
+ config = deepMerge(config, toolDefaults);
264
+
265
+ // 3. Merge project-specific configuration (highest priority)
115
266
  for (const fileName of CONFIG_FILES) {
116
267
  const configPath = join(cwd, fileName);
117
268
 
@@ -125,15 +276,15 @@ export async function loadConfig(cwd = process.cwd()) {
125
276
  projectConfig = await loadJsConfig(configPath);
126
277
  }
127
278
 
128
- return deepMerge(DEFAULTS, projectConfig);
279
+ return deepMerge(config, projectConfig);
129
280
  } catch (error) {
130
- // Log warning but continue with defaults
281
+ // Log warning but continue with detected defaults
131
282
  console.warn(`Warning: Failed to load ${fileName}: ${error.message}`);
132
283
  }
133
284
  }
134
285
  }
135
286
 
136
- return DEFAULTS;
287
+ return config;
137
288
  }
138
289
 
139
290
  /**
@@ -427,6 +427,7 @@ export function setupConfigs(projectDir, templatesDir, options) {
427
427
  // Other configs (same for all types)
428
428
  const otherConfigs = [
429
429
  {name: 'prettier.config.mjs', src: 'prettier.config.mjs'},
430
+ {name: '.prettierignore', src: '.prettierignore'},
430
431
  {name: 'commitlint.config.mjs', src: 'commitlint.config.mjs'},
431
432
  {name: 'lint-staged.config.mjs', src: 'lint-staged.config.mjs'},
432
433
  ];
@@ -0,0 +1,35 @@
1
+ # Dependencies
2
+ node_modules
3
+
4
+ # Build outputs
5
+ dist
6
+ build
7
+ out
8
+ .next
9
+ .nuxt
10
+
11
+ # Cache directories
12
+ .cache
13
+ .vite
14
+ .turbo
15
+ .parcel-cache
16
+
17
+ # Test coverage
18
+ coverage
19
+
20
+ # Package manager locks (optional - some prefer to format these)
21
+ pnpm-lock.yaml
22
+ package-lock.json
23
+ yarn.lock
24
+
25
+ # Auto-generated files
26
+ *.min.js
27
+ *.min.css
28
+
29
+ # IDE and editor
30
+ .idea
31
+ .vscode
32
+
33
+ # OS files
34
+ .DS_Store
35
+ Thumbs.db