@pattern-stack/codegen 0.6.0 → 0.6.1

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.
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Naming Configuration Loader
3
+ *
4
+ * Loads and validates backend naming configuration from codegen.config.yaml.
5
+ * Provides defaults matching current hardcoded behavior for backward compatibility.
6
+ *
7
+ * Usage:
8
+ * import { getNamingConfig, resolveLayerNaming } from './naming-config.mjs';
9
+ *
10
+ * const config = getNamingConfig();
11
+ * const domainNaming = resolveLayerNaming(config, 'domain');
12
+ */
13
+
14
+ import { projectConfig } from './config-loader.mjs';
15
+ import {
16
+ BackendNamingConfigSchema,
17
+ DEFAULT_BACKEND_NAMING,
18
+ resolveLayerNaming as resolveLayer,
19
+ } from '../schema/naming-config.schema.mjs';
20
+
21
+ // ============================================================================
22
+ // Deep Merge Utility
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Check if value is a plain object (not array, null, or other types)
27
+ */
28
+ function isPlainObject(obj) {
29
+ return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
30
+ }
31
+
32
+ /**
33
+ * Deep merge two objects, with source taking precedence
34
+ *
35
+ * - Recursively merges nested objects
36
+ * - Source values override target values
37
+ * - Handles null/undefined gracefully
38
+ *
39
+ * @param {object} target - Base object with defaults
40
+ * @param {object} source - Override object (takes precedence)
41
+ * @returns {object} Merged object
42
+ */
43
+ function deepMerge(target, source) {
44
+ if (source == null) return target;
45
+ if (target == null) return source;
46
+
47
+ const result = { ...target };
48
+
49
+ for (const key in source) {
50
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
51
+ const sourceValue = source[key];
52
+ const targetValue = target[key];
53
+
54
+ if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
55
+ result[key] = deepMerge(targetValue, sourceValue);
56
+ } else if (sourceValue !== undefined) {
57
+ result[key] = sourceValue;
58
+ }
59
+ }
60
+ }
61
+
62
+ return result;
63
+ }
64
+
65
+ // ============================================================================
66
+ // Configuration Loading
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Load and validate naming configuration
71
+ *
72
+ * - Reads `naming` section from project config
73
+ * - Deep merges with defaults for backward compatibility
74
+ * - Validates against Zod schema
75
+ * - Caches result for performance
76
+ *
77
+ * @returns {import('../schema/naming-config.schema.ts').BackendNamingConfig}
78
+ */
79
+ function loadNamingConfig() {
80
+ // Get naming section from project config (may be undefined)
81
+ const rawConfig = projectConfig?.naming;
82
+
83
+ // If no config, use defaults
84
+ if (!rawConfig) {
85
+ return BackendNamingConfigSchema.parse(DEFAULT_BACKEND_NAMING);
86
+ }
87
+
88
+ // Deep merge user config with defaults
89
+ const merged = deepMerge(DEFAULT_BACKEND_NAMING, rawConfig);
90
+
91
+ // Validate and return
92
+ try {
93
+ return BackendNamingConfigSchema.parse(merged);
94
+ } catch (error) {
95
+ console.error('Invalid naming configuration:');
96
+ if (error.errors) {
97
+ for (const err of error.errors) {
98
+ console.error(` - ${err.path.join('.')}: ${err.message}`);
99
+ }
100
+ }
101
+ throw new Error(
102
+ `Failed to load naming configuration: ${error.message}\n` +
103
+ 'Check your codegen.config.yaml naming section.'
104
+ );
105
+ }
106
+ }
107
+
108
+ // ============================================================================
109
+ // Cached Configuration
110
+ // ============================================================================
111
+
112
+ // Load config once at module initialization
113
+ let _cachedConfig = null;
114
+
115
+ /**
116
+ * Get the naming configuration
117
+ *
118
+ * Returns cached, validated config with all defaults applied.
119
+ * First call loads and validates; subsequent calls return cached value.
120
+ *
121
+ * NOTE: Config is cached at first access. Changes to codegen.config.yaml
122
+ * require restarting the CLI to take effect. Use clearNamingConfigCache()
123
+ * in tests to reset the cache between test runs.
124
+ *
125
+ * @returns {import('../schema/naming-config.schema.ts').BackendNamingConfig}
126
+ */
127
+ export function getNamingConfig() {
128
+ if (_cachedConfig === null) {
129
+ _cachedConfig = loadNamingConfig();
130
+ }
131
+ return _cachedConfig;
132
+ }
133
+
134
+ /**
135
+ * Clear cached config (useful for testing)
136
+ */
137
+ export function clearNamingConfigCache() {
138
+ _cachedConfig = null;
139
+ }
140
+
141
+ // ============================================================================
142
+ // Layer Resolution
143
+ // ============================================================================
144
+
145
+ /**
146
+ * Resolve effective naming config for a specific layer
147
+ *
148
+ * Merges layer-specific overrides with global defaults.
149
+ * Returns fully resolved config with no optional fields.
150
+ *
151
+ * @param {'domain' | 'application' | 'infrastructure' | 'presentation'} layer
152
+ * @returns {import('../schema/naming-config.schema.ts').ResolvedLayerNaming}
153
+ */
154
+ export function resolveLayerNaming(layer) {
155
+ const config = getNamingConfig();
156
+ return resolveLayer(config, layer);
157
+ }
158
+
159
+ // ============================================================================
160
+ // Exports
161
+ // ============================================================================
162
+
163
+ export {
164
+ DEFAULT_BACKEND_NAMING,
165
+ deepMerge,
166
+ };
167
+
168
+ export default {
169
+ getNamingConfig,
170
+ resolveLayerNaming,
171
+ clearNamingConfigCache,
172
+ DEFAULT_BACKEND_NAMING,
173
+ };