umwd-components 0.1.836 → 0.1.838
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/cjs/src/components/common/module-config/ModuleGuard.js +7 -0
- package/dist/cjs/src/index.js +1 -1
- package/dist/cjs/src/lib/module-config/config.js +6 -0
- package/dist/cjs/src/lib/module-config/navigation-filter.js +6 -0
- package/dist/cjs/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/src/components/common/module-config/ModuleGuard.js +39 -0
- package/dist/esm/src/index.js +3 -0
- package/dist/esm/src/lib/module-config/config.js +231 -0
- package/dist/esm/src/lib/module-config/navigation-filter.js +58 -0
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/types/components/common/module-config/ModuleGuard.d.ts +22 -0
- package/dist/esm/types/index.d.ts +4 -0
- package/dist/esm/types/lib/module-config/config.d.ts +33 -0
- package/dist/esm/types/lib/module-config/navigation-filter.d.ts +10 -0
- package/dist/esm/types/types/module-config/types.d.ts +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/*
|
|
3
|
+
* UMWD-Components
|
|
4
|
+
* @copyright Jelle Paulus
|
|
5
|
+
* @license MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { isModuleEnabled, isModuleFeatureEnabled } from '../../../lib/module-config/config.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Component that conditionally renders children based on module/feature enablement
|
|
13
|
+
*/
|
|
14
|
+
function ModuleGuard({ module, feature, fallback = null, children, }) {
|
|
15
|
+
const moduleEnabled = isModuleEnabled(module);
|
|
16
|
+
if (!moduleEnabled) {
|
|
17
|
+
return jsx(Fragment, { children: fallback });
|
|
18
|
+
}
|
|
19
|
+
if (feature) {
|
|
20
|
+
const featureEnabled = isModuleFeatureEnabled(module, feature);
|
|
21
|
+
if (!featureEnabled) {
|
|
22
|
+
return jsx(Fragment, { children: fallback });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return jsx(Fragment, { children: children });
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Render prop component that provides module/feature status to children
|
|
29
|
+
*/
|
|
30
|
+
function ConditionalModule({ module, feature, children, }) {
|
|
31
|
+
const moduleEnabled = isModuleEnabled(module);
|
|
32
|
+
const featureEnabled = feature
|
|
33
|
+
? isModuleFeatureEnabled(module, feature)
|
|
34
|
+
: true;
|
|
35
|
+
const isEnabled = moduleEnabled && featureEnabled;
|
|
36
|
+
return jsx(Fragment, { children: children(isEnabled) });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { ConditionalModule, ModuleGuard };
|
package/dist/esm/src/index.js
CHANGED
|
@@ -223,3 +223,6 @@ export { MinioItemList } from './components/common/media/minio/MinioItemList.js'
|
|
|
223
223
|
export { MinioDisplay } from './components/common/media/minio/MinioDisplay.js';
|
|
224
224
|
export { contactFormAction } from './data/actions/e-commerce/lead/contactFormAction.js';
|
|
225
225
|
export { ShippingStatusIndicator } from './components/e-commerce/opo/ShippingStatusIndicator.js';
|
|
226
|
+
export { clearConfigCache, getConfigFilePath, getEnabledModules, getFullConfig, getModuleConfig, isModuleEnabled, isModuleFeatureEnabled, validateCurrentConfig } from './lib/module-config/config.js';
|
|
227
|
+
export { filterNavigationByModules } from './lib/module-config/navigation-filter.js';
|
|
228
|
+
export { ConditionalModule, ModuleGuard } from './components/common/module-config/ModuleGuard.js';
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* UMWD-Components
|
|
3
|
+
* @copyright Jelle Paulus
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
let cachedConfig = null;
|
|
11
|
+
/**
|
|
12
|
+
* Get the path to the umwdconfig.json file
|
|
13
|
+
* This function works across different environments (frontend, backend, CMS)
|
|
14
|
+
*/
|
|
15
|
+
function getConfigPath() {
|
|
16
|
+
// Start from the current working directory
|
|
17
|
+
let currentPath = process.cwd();
|
|
18
|
+
// For apps in subdirectories, traverse up to find the root config
|
|
19
|
+
const possiblePaths = [
|
|
20
|
+
join(currentPath, "umwdconfig.json"), // Root level
|
|
21
|
+
join(currentPath, "..", "umwdconfig.json"), // One level up (for frontend/backend/cms)
|
|
22
|
+
join(currentPath, "..", "..", "umwdconfig.json"), // Two levels up
|
|
23
|
+
join(currentPath, "..", "..", "..", "umwdconfig.json"), // Three levels up
|
|
24
|
+
];
|
|
25
|
+
for (const path of possiblePaths) {
|
|
26
|
+
try {
|
|
27
|
+
// Check if file exists by trying to read it
|
|
28
|
+
readFileSync(path, "utf8");
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// File doesn't exist, continue to next path
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw new Error("umwdconfig.json not found. Please ensure the configuration file exists in the project root.\n" +
|
|
37
|
+
`Searched paths:\n${possiblePaths.map((p) => ` - ${p}`).join("\n")}`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate the UMWD configuration object
|
|
41
|
+
*/
|
|
42
|
+
function validateConfig(config) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
if (!config || typeof config !== "object") {
|
|
45
|
+
return {
|
|
46
|
+
isValid: false,
|
|
47
|
+
errors: [
|
|
48
|
+
{ field: "root", message: "Configuration must be a valid object" },
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (!config.modules || typeof config.modules !== "object") {
|
|
53
|
+
errors.push({
|
|
54
|
+
field: "modules",
|
|
55
|
+
message: "modules field is required and must be an object",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Validate required modules
|
|
60
|
+
const requiredModules = ["pageBuilder", "ecommerce"];
|
|
61
|
+
for (const module of requiredModules) {
|
|
62
|
+
if (!config.modules[module]) {
|
|
63
|
+
errors.push({
|
|
64
|
+
field: `modules.${module}`,
|
|
65
|
+
message: `${module} module configuration is required`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else if (typeof config.modules[module] !== "object") {
|
|
69
|
+
errors.push({
|
|
70
|
+
field: `modules.${module}`,
|
|
71
|
+
message: `${module} module must be an object`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (typeof config.modules[module].enabled !== "boolean") {
|
|
75
|
+
errors.push({
|
|
76
|
+
field: `modules.${module}.enabled`,
|
|
77
|
+
message: `${module}.enabled must be a boolean`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Validate optional features within modules
|
|
82
|
+
Object.keys(config.modules).forEach((moduleName) => {
|
|
83
|
+
const module = config.modules[moduleName];
|
|
84
|
+
if (module && module.features && typeof module.features !== "object") {
|
|
85
|
+
errors.push({
|
|
86
|
+
field: `modules.${moduleName}.features`,
|
|
87
|
+
message: `${moduleName}.features must be an object`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (module && module.features) {
|
|
91
|
+
Object.keys(module.features).forEach((featureName) => {
|
|
92
|
+
if (typeof module.features[featureName] !== "boolean") {
|
|
93
|
+
errors.push({
|
|
94
|
+
field: `modules.${moduleName}.features.${featureName}`,
|
|
95
|
+
message: `${moduleName}.features.${featureName} must be a boolean`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
isValid: errors.length === 0,
|
|
104
|
+
errors,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Load and parse the UMWD configuration file
|
|
109
|
+
*/
|
|
110
|
+
function loadConfig(useCache = true) {
|
|
111
|
+
if (useCache && cachedConfig) {
|
|
112
|
+
return cachedConfig;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const configPath = getConfigPath();
|
|
116
|
+
const configContent = readFileSync(configPath, "utf8");
|
|
117
|
+
const config = JSON.parse(configContent);
|
|
118
|
+
const validation = validateConfig(config);
|
|
119
|
+
if (!validation.isValid) {
|
|
120
|
+
const errorMessages = validation.errors
|
|
121
|
+
.map((error) => `${error.field}: ${error.message}`)
|
|
122
|
+
.join(", ");
|
|
123
|
+
throw new Error(`Invalid UMWD configuration: ${errorMessages}`);
|
|
124
|
+
}
|
|
125
|
+
cachedConfig = config;
|
|
126
|
+
return cachedConfig;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error instanceof SyntaxError) {
|
|
130
|
+
throw new Error("Invalid JSON in umwdconfig.json file. Please check the file syntax.");
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get the configuration for a specific module
|
|
137
|
+
*/
|
|
138
|
+
function getModuleConfig(moduleName) {
|
|
139
|
+
const config = loadConfig();
|
|
140
|
+
if (!config.modules[moduleName]) {
|
|
141
|
+
throw new Error(`Module '${moduleName}' not found in configuration`);
|
|
142
|
+
}
|
|
143
|
+
return config.modules[moduleName];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Check if a specific module is enabled
|
|
147
|
+
*/
|
|
148
|
+
function isModuleEnabled(moduleName) {
|
|
149
|
+
try {
|
|
150
|
+
const moduleConfig = getModuleConfig(moduleName);
|
|
151
|
+
return moduleConfig ? moduleConfig.enabled : false;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// If module doesn't exist or config is invalid, assume it's disabled
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if a specific feature within a module is enabled
|
|
160
|
+
*/
|
|
161
|
+
function isModuleFeatureEnabled(moduleName, featureName) {
|
|
162
|
+
try {
|
|
163
|
+
const moduleConfig = getModuleConfig(moduleName);
|
|
164
|
+
// If module doesn't exist or is disabled, all features are disabled
|
|
165
|
+
if (!moduleConfig || !moduleConfig.enabled) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
// If no features are defined, assume the feature is enabled (if module is enabled)
|
|
169
|
+
if (!moduleConfig.features) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
// Check if the specific feature is enabled
|
|
173
|
+
return moduleConfig.features[featureName] === true;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// If module doesn't exist or config is invalid, assume feature is disabled
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get the full configuration object
|
|
182
|
+
*/
|
|
183
|
+
function getFullConfig() {
|
|
184
|
+
return loadConfig();
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get all enabled modules
|
|
188
|
+
*/
|
|
189
|
+
function getEnabledModules() {
|
|
190
|
+
const config = loadConfig();
|
|
191
|
+
return Object.keys(config.modules).filter((moduleName) => {
|
|
192
|
+
const moduleConfig = config.modules[moduleName];
|
|
193
|
+
return moduleConfig && moduleConfig.enabled;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate the current configuration without throwing
|
|
198
|
+
*/
|
|
199
|
+
function validateCurrentConfig() {
|
|
200
|
+
try {
|
|
201
|
+
const configPath = getConfigPath();
|
|
202
|
+
const configContent = readFileSync(configPath, "utf8");
|
|
203
|
+
const config = JSON.parse(configContent);
|
|
204
|
+
return validateConfig(config);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
isValid: false,
|
|
209
|
+
errors: [
|
|
210
|
+
{
|
|
211
|
+
field: "file",
|
|
212
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Clear the configuration cache (useful for testing or dynamic config reloading)
|
|
220
|
+
*/
|
|
221
|
+
function clearConfigCache() {
|
|
222
|
+
cachedConfig = null;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get the path to the configuration file (useful for debugging)
|
|
226
|
+
*/
|
|
227
|
+
function getConfigFilePath() {
|
|
228
|
+
return getConfigPath();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { clearConfigCache, getConfigFilePath, getEnabledModules, getFullConfig, getModuleConfig, isModuleEnabled, isModuleFeatureEnabled, validateCurrentConfig };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* UMWD-Components
|
|
3
|
+
* @copyright Jelle Paulus
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { isModuleEnabled, isModuleFeatureEnabled } from './config.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Filter navigation items based on enabled modules and features
|
|
11
|
+
*/
|
|
12
|
+
function filterNavigationByModules(navigation) {
|
|
13
|
+
return navigation
|
|
14
|
+
.filter((item) => {
|
|
15
|
+
// Check if this is an e-commerce related navigation item
|
|
16
|
+
if (item.href && isECommerceRoute(item.href)) {
|
|
17
|
+
if (!isModuleEnabled("ecommerce")) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
// Check for specific e-commerce features
|
|
21
|
+
if (item.href.includes("/user/orders") ||
|
|
22
|
+
item.href.includes("/user/returns")) {
|
|
23
|
+
return isModuleFeatureEnabled("ecommerce", "orders");
|
|
24
|
+
}
|
|
25
|
+
if (item.href.includes("/shop")) {
|
|
26
|
+
return isModuleFeatureEnabled("ecommerce", "shopping");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
})
|
|
31
|
+
.map((item) => {
|
|
32
|
+
// Process children for items that passed the filter
|
|
33
|
+
if (item.children) {
|
|
34
|
+
return {
|
|
35
|
+
...item,
|
|
36
|
+
children: filterNavigationByModules(item.children),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return item;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a route is e-commerce related
|
|
44
|
+
*/
|
|
45
|
+
function isECommerceRoute(href) {
|
|
46
|
+
const ecommercePatterns = [
|
|
47
|
+
"/shop",
|
|
48
|
+
"/user/orders",
|
|
49
|
+
"/user/returns",
|
|
50
|
+
"/user/profile",
|
|
51
|
+
"/cart",
|
|
52
|
+
"/checkout",
|
|
53
|
+
"/check-out",
|
|
54
|
+
];
|
|
55
|
+
return ecommercePatterns.some((pattern) => href.startsWith(pattern));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { filterNavigationByModules };
|