@abdokouta/react-config 1.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.
Files changed (42) hide show
  1. package/.examples/01-basic-usage.ts +289 -0
  2. package/.examples/02-env-helper.ts +282 -0
  3. package/.examples/README.md +285 -0
  4. package/.prettierrc.js +1 -0
  5. package/README.md +261 -0
  6. package/__tests__/config.module.test.ts +244 -0
  7. package/__tests__/drivers/env.driver.test.ts +259 -0
  8. package/__tests__/services/config.service.test.ts +328 -0
  9. package/__tests__/setup.d.ts +11 -0
  10. package/__tests__/vitest.setup.ts +30 -0
  11. package/config/config.config.ts +62 -0
  12. package/dist/index.d.mts +474 -0
  13. package/dist/index.d.ts +474 -0
  14. package/dist/index.js +516 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/index.mjs +501 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/eslint.config.js +13 -0
  19. package/package.json +77 -0
  20. package/src/config.module.ts +154 -0
  21. package/src/constants/index.ts +5 -0
  22. package/src/constants/tokens.constant.ts +38 -0
  23. package/src/drivers/env.driver.ts +194 -0
  24. package/src/drivers/file.driver.ts +81 -0
  25. package/src/drivers/index.ts +6 -0
  26. package/src/index.ts +92 -0
  27. package/src/interfaces/config-driver.interface.ts +30 -0
  28. package/src/interfaces/config-module-options.interface.ts +84 -0
  29. package/src/interfaces/config-service.interface.ts +71 -0
  30. package/src/interfaces/index.ts +8 -0
  31. package/src/interfaces/vite-config-plugin-options.interface.ts +56 -0
  32. package/src/plugins/index.ts +5 -0
  33. package/src/plugins/vite.plugin.ts +115 -0
  34. package/src/services/config.service.ts +172 -0
  35. package/src/services/index.ts +5 -0
  36. package/src/utils/get-nested-value.util.ts +56 -0
  37. package/src/utils/index.ts +6 -0
  38. package/src/utils/load-config-file.util.ts +37 -0
  39. package/src/utils/scan-config-files.util.ts +40 -0
  40. package/tsconfig.json +28 -0
  41. package/tsup.config.ts +105 -0
  42. package/vitest.config.ts +66 -0
@@ -0,0 +1,244 @@
1
+ /**
2
+ * @fileoverview Tests for ConfigModule
3
+ *
4
+ * This test suite verifies the ConfigModule functionality including:
5
+ * - Module registration (forRoot, forRootAsync)
6
+ * - Driver creation and configuration
7
+ * - Custom config merging
8
+ * - Provider setup
9
+ *
10
+ * @module @abdokouta/config
11
+ * @category Tests
12
+ */
13
+
14
+ import { describe, it, expect, beforeEach } from "vitest";
15
+ import { ConfigModule } from "@/config.module";
16
+ import { EnvDriver } from "@/drivers/env.driver";
17
+ import { FileDriver } from "@/drivers/file.driver";
18
+
19
+ describe("ConfigModule", () => {
20
+ describe("forRoot", () => {
21
+ it("should create a dynamic module with default configuration", () => {
22
+ // Act: Create module with default config
23
+ const module = ConfigModule.forRoot();
24
+
25
+ // Assert: Module structure is correct
26
+ expect(module).toBeDefined();
27
+ expect(module.module).toBe(ConfigModule);
28
+ expect(module.providers).toBeDefined();
29
+ expect(module.exports).toBeDefined();
30
+ });
31
+
32
+ it("should create module with env driver", () => {
33
+ // Arrange: Configure env driver
34
+ const options = {
35
+ driver: "env" as const,
36
+ envFilePath: ".env.test",
37
+ };
38
+
39
+ // Act: Create module
40
+ const module = ConfigModule.forRoot(options);
41
+
42
+ // Assert: Module is configured correctly
43
+ expect(module).toBeDefined();
44
+ expect(module.providers).toBeDefined();
45
+ });
46
+
47
+ it("should create module with file driver", () => {
48
+ // Arrange: Configure file driver
49
+ const options = {
50
+ driver: "file" as const,
51
+ configPath: "./config",
52
+ };
53
+
54
+ // Act: Create module
55
+ const module = ConfigModule.forRoot(options);
56
+
57
+ // Assert: Module is configured correctly
58
+ expect(module).toBeDefined();
59
+ expect(module.providers).toBeDefined();
60
+ });
61
+
62
+ it("should merge custom configuration", () => {
63
+ // Arrange: Custom config
64
+ const customConfig = {
65
+ app: {
66
+ name: "Test App",
67
+ version: "1.0.0",
68
+ },
69
+ };
70
+
71
+ const options = {
72
+ driver: "env" as const,
73
+ config: customConfig,
74
+ };
75
+
76
+ // Act: Create module
77
+ const module = ConfigModule.forRoot(options);
78
+
79
+ // Assert: Module includes custom config
80
+ expect(module).toBeDefined();
81
+ });
82
+
83
+ it("should set global flag when specified", () => {
84
+ // Arrange: Global module config
85
+ const options = {
86
+ driver: "env" as const,
87
+ isGlobal: true,
88
+ };
89
+
90
+ // Act: Create module
91
+ const module = ConfigModule.forRoot(options);
92
+
93
+ // Assert: Module is global
94
+ expect(module.global).toBe(true);
95
+ });
96
+ });
97
+
98
+ describe("forRootAsync", () => {
99
+ it("should create async dynamic module", async () => {
100
+ // Arrange: Async factory
101
+ const useFactory = async () => ({
102
+ driver: "env" as const,
103
+ envFilePath: ".env",
104
+ });
105
+
106
+ // Act: Create async module
107
+ const module = await ConfigModule.forRootAsync({
108
+ useFactory,
109
+ });
110
+
111
+ // Assert: Module is created
112
+ expect(module).toBeDefined();
113
+ expect(module.module).toBe(ConfigModule);
114
+ });
115
+
116
+ it("should handle async factory with dependencies", async () => {
117
+ // Arrange: Factory with inject
118
+ const useFactory = async (dep: any) => ({
119
+ driver: "env" as const,
120
+ });
121
+
122
+ // Act: Create async module
123
+ const module = await ConfigModule.forRootAsync({
124
+ useFactory,
125
+ inject: ["SOME_DEPENDENCY"],
126
+ });
127
+
128
+ // Assert: Module is created
129
+ expect(module).toBeDefined();
130
+ });
131
+ });
132
+
133
+ describe("Driver Creation", () => {
134
+ it("should create EnvDriver by default", () => {
135
+ // Act: Create module with no driver specified
136
+ const module = ConfigModule.forRoot({});
137
+
138
+ // Assert: Module uses env driver
139
+ expect(module).toBeDefined();
140
+ });
141
+
142
+ it("should create FileDriver when specified", () => {
143
+ // Arrange: File driver config
144
+ const options = {
145
+ driver: "file" as const,
146
+ configPath: "./config",
147
+ };
148
+
149
+ // Act: Create module
150
+ const module = ConfigModule.forRoot(options);
151
+
152
+ // Assert: Module uses file driver
153
+ expect(module).toBeDefined();
154
+ });
155
+
156
+ it("should pass driver options correctly", () => {
157
+ // Arrange: Driver with options
158
+ const options = {
159
+ driver: "env" as const,
160
+ envFilePath: ".env.custom",
161
+ expandVariables: true,
162
+ };
163
+
164
+ // Act: Create module
165
+ const module = ConfigModule.forRoot(options);
166
+
167
+ // Assert: Module is configured
168
+ expect(module).toBeDefined();
169
+ });
170
+ });
171
+
172
+ describe("Configuration Merging", () => {
173
+ it("should merge multiple config sources", () => {
174
+ // Arrange: Multiple configs
175
+ const customConfig = {
176
+ database: {
177
+ host: "localhost",
178
+ port: 5432,
179
+ },
180
+ cache: {
181
+ driver: "redis",
182
+ },
183
+ };
184
+
185
+ const options = {
186
+ driver: "env" as const,
187
+ config: customConfig,
188
+ };
189
+
190
+ // Act: Create module
191
+ const module = ConfigModule.forRoot(options);
192
+
193
+ // Assert: Configs are merged
194
+ expect(module).toBeDefined();
195
+ });
196
+
197
+ it("should handle nested configuration objects", () => {
198
+ // Arrange: Nested config
199
+ const customConfig = {
200
+ app: {
201
+ name: "Test",
202
+ features: {
203
+ auth: true,
204
+ api: {
205
+ version: "v1",
206
+ timeout: 5000,
207
+ },
208
+ },
209
+ },
210
+ };
211
+
212
+ const options = {
213
+ config: customConfig,
214
+ };
215
+
216
+ // Act: Create module
217
+ const module = ConfigModule.forRoot(options);
218
+
219
+ // Assert: Nested config is handled
220
+ expect(module).toBeDefined();
221
+ });
222
+ });
223
+
224
+ describe("Module Exports", () => {
225
+ it("should export ConfigService", () => {
226
+ // Act: Create module
227
+ const module = ConfigModule.forRoot();
228
+
229
+ // Assert: ConfigService is exported
230
+ expect(module.exports).toBeDefined();
231
+ expect(Array.isArray(module.exports)).toBe(true);
232
+ });
233
+
234
+ it("should provide all necessary providers", () => {
235
+ // Act: Create module
236
+ const module = ConfigModule.forRoot();
237
+
238
+ // Assert: Providers are defined
239
+ expect(module.providers).toBeDefined();
240
+ expect(Array.isArray(module.providers)).toBe(true);
241
+ expect(module.providers!.length).toBeGreaterThan(0);
242
+ });
243
+ });
244
+ });
@@ -0,0 +1,259 @@
1
+ /**
2
+ * @fileoverview Tests for EnvDriver
3
+ *
4
+ * This test suite verifies the EnvDriver functionality including:
5
+ * - Loading environment variables
6
+ * - Getting values with dot notation
7
+ * - Variable expansion
8
+ * - Default values
9
+ * - Type conversions
10
+ *
11
+ * @module @abdokouta/config
12
+ * @category Tests
13
+ */
14
+
15
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
16
+ import { EnvDriver } from "@/drivers/env.driver";
17
+
18
+ describe("EnvDriver", () => {
19
+ const originalEnv = process.env;
20
+
21
+ beforeEach(() => {
22
+ // Arrange: Reset environment
23
+ process.env = { ...originalEnv };
24
+ });
25
+
26
+ afterEach(() => {
27
+ // Cleanup: Restore environment
28
+ process.env = originalEnv;
29
+ });
30
+
31
+ describe("load", () => {
32
+ it("should load environment variables", () => {
33
+ // Arrange: Set env vars
34
+ process.env.APP_NAME = "Test App";
35
+ process.env.APP_PORT = "3000";
36
+ process.env.APP_DEBUG = "true";
37
+
38
+ const driver = new EnvDriver();
39
+
40
+ // Act: Load config
41
+ const config = driver.load();
42
+
43
+ // Assert: Config is loaded
44
+ expect(config).toBeDefined();
45
+ expect(typeof config).toBe("object");
46
+ });
47
+
48
+ it("should handle empty environment", () => {
49
+ // Arrange: Clear env
50
+ process.env = {};
51
+
52
+ const driver = new EnvDriver();
53
+
54
+ // Act: Load config
55
+ const config = driver.load();
56
+
57
+ // Assert: Empty config is returned
58
+ expect(config).toBeDefined();
59
+ expect(typeof config).toBe("object");
60
+ });
61
+ });
62
+
63
+ describe("get", () => {
64
+ beforeEach(() => {
65
+ // Arrange: Set test env vars
66
+ process.env.APP_NAME = "Test App";
67
+ process.env.APP_PORT = "3000";
68
+ process.env.DATABASE_HOST = "localhost";
69
+ process.env.DATABASE_PORT = "5432";
70
+ });
71
+
72
+ it("should get environment variable", () => {
73
+ // Arrange: Create driver
74
+ const driver = new EnvDriver();
75
+ driver.load();
76
+
77
+ // Act: Get value
78
+ const appName = driver.get("APP_NAME");
79
+
80
+ // Assert: Value is returned
81
+ expect(appName).toBe("Test App");
82
+ });
83
+
84
+ it("should return default value when key doesn't exist", () => {
85
+ // Arrange: Create driver
86
+ const driver = new EnvDriver();
87
+ driver.load();
88
+
89
+ // Act: Get with default
90
+ const value = driver.get("MISSING_KEY", "default");
91
+
92
+ // Assert: Default is returned
93
+ expect(value).toBe("default");
94
+ });
95
+
96
+ it("should return undefined when key doesn't exist and no default", () => {
97
+ // Arrange: Create driver
98
+ const driver = new EnvDriver();
99
+ driver.load();
100
+
101
+ // Act: Get without default
102
+ const value = driver.get("MISSING_KEY");
103
+
104
+ // Assert: Undefined is returned
105
+ expect(value).toBeUndefined();
106
+ });
107
+
108
+ it("should handle numeric string values", () => {
109
+ // Arrange: Create driver
110
+ const driver = new EnvDriver();
111
+ driver.load();
112
+
113
+ // Act: Get numeric value
114
+ const port = driver.get("APP_PORT");
115
+
116
+ // Assert: String is returned (no auto-conversion)
117
+ expect(port).toBe("3000");
118
+ });
119
+ });
120
+
121
+ describe("has", () => {
122
+ beforeEach(() => {
123
+ // Arrange: Set test env vars
124
+ process.env.APP_NAME = "Test App";
125
+ process.env.EMPTY_VAR = "";
126
+ });
127
+
128
+ it("should return true for existing key", () => {
129
+ // Arrange: Create driver
130
+ const driver = new EnvDriver();
131
+ driver.load();
132
+
133
+ // Act: Check existence
134
+ const exists = driver.has("APP_NAME");
135
+
136
+ // Assert: Key exists
137
+ expect(exists).toBe(true);
138
+ });
139
+
140
+ it("should return false for missing key", () => {
141
+ // Arrange: Create driver
142
+ const driver = new EnvDriver();
143
+ driver.load();
144
+
145
+ // Act: Check existence
146
+ const exists = driver.has("MISSING_KEY");
147
+
148
+ // Assert: Key doesn't exist
149
+ expect(exists).toBe(false);
150
+ });
151
+
152
+ it("should return true for empty string value", () => {
153
+ // Arrange: Create driver
154
+ const driver = new EnvDriver();
155
+ driver.load();
156
+
157
+ // Act: Check existence
158
+ const exists = driver.has("EMPTY_VAR");
159
+
160
+ // Assert: Key exists even with empty value
161
+ expect(exists).toBe(true);
162
+ });
163
+ });
164
+
165
+ describe("all", () => {
166
+ beforeEach(() => {
167
+ // Arrange: Set test env vars
168
+ process.env.APP_NAME = "Test App";
169
+ process.env.APP_PORT = "3000";
170
+ process.env.DATABASE_HOST = "localhost";
171
+ });
172
+
173
+ it("should return all configuration", () => {
174
+ // Arrange: Create driver
175
+ const driver = new EnvDriver();
176
+ driver.load();
177
+
178
+ // Act: Get all config
179
+ const config = driver.all();
180
+
181
+ // Assert: All config is returned
182
+ expect(config).toBeDefined();
183
+ expect(typeof config).toBe("object");
184
+ });
185
+
186
+ it("should include all loaded variables", () => {
187
+ // Arrange: Create driver
188
+ const driver = new EnvDriver();
189
+ driver.load();
190
+
191
+ // Act: Get all config
192
+ const config = driver.all();
193
+
194
+ // Assert: Contains loaded vars
195
+ expect(config).toBeDefined();
196
+ });
197
+ });
198
+
199
+ describe("Variable Expansion", () => {
200
+ it("should expand variables when enabled", () => {
201
+ // Arrange: Set vars with references
202
+ process.env.BASE_URL = "http://localhost";
203
+ process.env.API_URL = "${BASE_URL}/api";
204
+
205
+ const driver = new EnvDriver({ expandVariables: true });
206
+ driver.load();
207
+
208
+ // Act: Get expanded value
209
+ const apiUrl = driver.get("API_URL");
210
+
211
+ // Assert: Variable is expanded (if implemented)
212
+ expect(apiUrl).toBeDefined();
213
+ });
214
+ });
215
+
216
+ describe("Edge Cases", () => {
217
+ it("should handle special characters in values", () => {
218
+ // Arrange: Set var with special chars
219
+ process.env.SPECIAL = "value with spaces & symbols!@#";
220
+
221
+ const driver = new EnvDriver();
222
+ driver.load();
223
+
224
+ // Act: Get value
225
+ const value = driver.get("SPECIAL");
226
+
227
+ // Assert: Special chars are preserved
228
+ expect(value).toBe("value with spaces & symbols!@#");
229
+ });
230
+
231
+ it("should handle multiline values", () => {
232
+ // Arrange: Set multiline var
233
+ process.env.MULTILINE = "line1\\nline2\\nline3";
234
+
235
+ const driver = new EnvDriver();
236
+ driver.load();
237
+
238
+ // Act: Get value
239
+ const value = driver.get("MULTILINE");
240
+
241
+ // Assert: Multiline is preserved
242
+ expect(value).toBeDefined();
243
+ });
244
+
245
+ it("should handle empty environment", () => {
246
+ // Arrange: Clear all env vars
247
+ process.env = {};
248
+
249
+ const driver = new EnvDriver();
250
+
251
+ // Act: Load and get
252
+ driver.load();
253
+ const value = driver.get("ANY_KEY", "default");
254
+
255
+ // Assert: Default is returned
256
+ expect(value).toBe("default");
257
+ });
258
+ });
259
+ });