@burgan-tech/vnext-workflow-cli 1.0.0 → 1.0.2
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/.github/CODEOWNERS +2 -0
- package/README.md +244 -82
- package/bin/workflow.js +0 -0
- package/package.json +5 -5
- package/src/commands/check.js +62 -22
- package/src/commands/config.js +5 -6
- package/src/commands/csx.js +125 -44
- package/src/commands/reset.js +198 -80
- package/src/commands/sync.js +189 -107
- package/src/commands/update.js +217 -99
- package/src/lib/api.js +52 -34
- package/src/lib/config.js +43 -5
- package/src/lib/csx.js +130 -57
- package/src/lib/discover.js +131 -29
- package/src/lib/vnextConfig.js +124 -0
- package/src/lib/workflow.js +86 -39
package/src/lib/csx.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { glob } = require('glob');
|
|
4
|
+
const { discoverComponents, findAllJsonFiles } = require('./discover');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* CSX
|
|
7
|
+
* Encodes CSX file to Base64
|
|
8
|
+
* @param {string} csxPath - CSX file path
|
|
9
|
+
* @returns {Promise<string>} Base64 encoded content
|
|
7
10
|
*/
|
|
8
11
|
async function encodeToBase64(csxPath) {
|
|
9
12
|
const content = await fs.readFile(csxPath, 'utf8');
|
|
@@ -11,28 +14,32 @@ async function encodeToBase64(csxPath) {
|
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
|
-
*
|
|
17
|
+
* Finds JSON files that reference the CSX file
|
|
18
|
+
* Only searches in paths defined in vnext.config.json
|
|
19
|
+
* @param {string} csxPath - CSX file path
|
|
20
|
+
* @param {string} projectRoot - Project root folder
|
|
21
|
+
* @returns {Promise<string[]>} Matching JSON file paths
|
|
15
22
|
*/
|
|
16
23
|
async function findJsonFilesForCsx(csxPath, projectRoot) {
|
|
17
24
|
const csxBaseName = path.basename(csxPath);
|
|
18
25
|
|
|
19
|
-
//
|
|
20
|
-
const
|
|
21
|
-
const jsonFiles = await glob(pattern, {
|
|
22
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/package*.json', '**/*config*.json']
|
|
23
|
-
});
|
|
26
|
+
// Get discovered components (only paths defined in vnext.config.json)
|
|
27
|
+
const discovered = await discoverComponents(projectRoot);
|
|
24
28
|
|
|
25
|
-
//
|
|
29
|
+
// Get all JSON files from discovered components only
|
|
30
|
+
const jsonFileInfos = await findAllJsonFiles(discovered);
|
|
31
|
+
|
|
32
|
+
// Filter JSONs that reference the CSX
|
|
26
33
|
const matchingJsons = [];
|
|
27
34
|
|
|
28
|
-
for (const
|
|
35
|
+
for (const jsonInfo of jsonFileInfos) {
|
|
29
36
|
try {
|
|
30
|
-
const content = await fs.readFile(
|
|
37
|
+
const content = await fs.readFile(jsonInfo.path, 'utf8');
|
|
31
38
|
if (content.includes(csxBaseName)) {
|
|
32
|
-
matchingJsons.push(
|
|
39
|
+
matchingJsons.push(jsonInfo.path);
|
|
33
40
|
}
|
|
34
41
|
} catch (error) {
|
|
35
|
-
//
|
|
42
|
+
// Skip unreadable files
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
@@ -40,11 +47,14 @@ async function findJsonFilesForCsx(csxPath, projectRoot) {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
|
-
* CSX location path
|
|
50
|
+
* Calculates CSX location path
|
|
51
|
+
* @param {string} csxPath - CSX file path
|
|
52
|
+
* @param {string} projectRoot - Project root folder
|
|
53
|
+
* @returns {string} Location path
|
|
44
54
|
*/
|
|
45
|
-
function getCsxLocation(csxPath) {
|
|
46
|
-
// ./src/Rules/MyRule.csx
|
|
47
|
-
const parts = csxPath.split(
|
|
55
|
+
function getCsxLocation(csxPath, projectRoot) {
|
|
56
|
+
// Convert to ./src/Rules/MyRule.csx format
|
|
57
|
+
const parts = csxPath.split(path.sep);
|
|
48
58
|
const srcIndex = parts.lastIndexOf('src');
|
|
49
59
|
|
|
50
60
|
if (srcIndex !== -1) {
|
|
@@ -56,91 +66,154 @@ function getCsxLocation(csxPath) {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/**
|
|
59
|
-
*
|
|
69
|
+
* Updates ALL CSX codes in a JSON file
|
|
70
|
+
* Updates all references with the same location
|
|
71
|
+
* Supports encoding types: NAT (native/plain text), B64 (Base64, default)
|
|
72
|
+
* @param {string} jsonPath - JSON file path
|
|
73
|
+
* @param {string} csxLocation - CSX location path
|
|
74
|
+
* @param {string} base64Code - Base64 encoded CSX content
|
|
75
|
+
* @param {string} nativeCode - Native (plain text) CSX content
|
|
76
|
+
* @returns {Promise<number>} Number of updated references
|
|
60
77
|
*/
|
|
61
|
-
async function updateCodeInJson(jsonPath, csxLocation, base64Code) {
|
|
78
|
+
async function updateCodeInJson(jsonPath, csxLocation, base64Code, nativeCode) {
|
|
62
79
|
const content = await fs.readFile(jsonPath, 'utf8');
|
|
63
80
|
const data = JSON.parse(content);
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
let updateCount = 0;
|
|
83
|
+
|
|
84
|
+
// Recursively find and update ALL location matches
|
|
66
85
|
function updateRecursive(obj) {
|
|
67
|
-
if (typeof obj !== 'object' || obj === null) return
|
|
86
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
87
|
+
|
|
88
|
+
// Skip if location is missing
|
|
89
|
+
if (!obj.location) {
|
|
90
|
+
// Continue scanning children
|
|
91
|
+
for (const key in obj) {
|
|
92
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
93
|
+
updateRecursive(obj[key]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
68
98
|
|
|
69
99
|
if (obj.location === csxLocation && 'code' in obj) {
|
|
70
|
-
|
|
71
|
-
|
|
100
|
+
// Check encoding type: NAT = Native (plain text), B64 or empty = Base64 (default)
|
|
101
|
+
const encoding = obj.encoding ? obj.encoding.toUpperCase() : 'B64';
|
|
102
|
+
|
|
103
|
+
if (encoding === 'NAT') {
|
|
104
|
+
// Native encoding - write plain text content
|
|
105
|
+
obj.code = nativeCode;
|
|
106
|
+
} else {
|
|
107
|
+
// B64 or default - write Base64 encoded content
|
|
108
|
+
obj.code = base64Code;
|
|
109
|
+
}
|
|
110
|
+
updateCount++;
|
|
72
111
|
}
|
|
73
112
|
|
|
113
|
+
// Scan all elements in array or object
|
|
74
114
|
for (const key in obj) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
115
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
116
|
+
updateRecursive(obj[key]);
|
|
77
117
|
}
|
|
78
118
|
}
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
119
|
}
|
|
82
120
|
|
|
83
|
-
|
|
121
|
+
updateRecursive(data);
|
|
84
122
|
|
|
85
|
-
if (
|
|
123
|
+
if (updateCount > 0) {
|
|
86
124
|
await fs.writeFile(jsonPath, JSON.stringify(data, null, 2), 'utf8');
|
|
87
|
-
return true;
|
|
88
125
|
}
|
|
89
126
|
|
|
90
|
-
return
|
|
127
|
+
return updateCount;
|
|
91
128
|
}
|
|
92
129
|
|
|
93
130
|
/**
|
|
94
|
-
*
|
|
131
|
+
* Reads CSX file content as plain text (native)
|
|
132
|
+
* @param {string} csxPath - CSX file path
|
|
133
|
+
* @returns {Promise<string>} Plain text content
|
|
134
|
+
*/
|
|
135
|
+
async function readNativeContent(csxPath) {
|
|
136
|
+
return await fs.readFile(csxPath, 'utf8');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Processes a single CSX file
|
|
141
|
+
* Updates ALL referencing JSON files
|
|
142
|
+
* Supports both NAT (native) and B64 (Base64) encoding
|
|
143
|
+
* @param {string} csxPath - CSX file path
|
|
144
|
+
* @param {string} projectRoot - Project root folder
|
|
145
|
+
* @returns {Promise<Object>} Process result
|
|
95
146
|
*/
|
|
96
147
|
async function processCsxFile(csxPath, projectRoot) {
|
|
97
|
-
//
|
|
98
|
-
const
|
|
148
|
+
// Read native content
|
|
149
|
+
const nativeCode = await readNativeContent(csxPath);
|
|
99
150
|
|
|
100
|
-
//
|
|
151
|
+
// Convert to Base64
|
|
152
|
+
const base64Code = Buffer.from(nativeCode).toString('base64');
|
|
153
|
+
|
|
154
|
+
// Find ALL related JSONs (only in paths defined in vnext.config.json)
|
|
101
155
|
const jsonFiles = await findJsonFilesForCsx(csxPath, projectRoot);
|
|
102
156
|
|
|
103
157
|
if (jsonFiles.length === 0) {
|
|
104
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: 'No related JSON found',
|
|
161
|
+
updatedJsonCount: 0,
|
|
162
|
+
totalUpdates: 0,
|
|
163
|
+
jsonFiles: []
|
|
164
|
+
};
|
|
105
165
|
}
|
|
106
166
|
|
|
107
|
-
// CSX location
|
|
108
|
-
const csxLocation = getCsxLocation(csxPath);
|
|
167
|
+
// Calculate CSX location
|
|
168
|
+
const csxLocation = getCsxLocation(csxPath, projectRoot);
|
|
169
|
+
|
|
170
|
+
// Update each JSON
|
|
171
|
+
let updatedJsonCount = 0;
|
|
172
|
+
let totalUpdates = 0;
|
|
173
|
+
const updatedFiles = [];
|
|
109
174
|
|
|
110
|
-
// Her JSON'u güncelle
|
|
111
|
-
let updatedCount = 0;
|
|
112
175
|
for (const jsonFile of jsonFiles) {
|
|
113
176
|
try {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
|
|
177
|
+
const updates = await updateCodeInJson(jsonFile, csxLocation, base64Code, nativeCode);
|
|
178
|
+
if (updates > 0) {
|
|
179
|
+
updatedJsonCount++;
|
|
180
|
+
totalUpdates += updates;
|
|
181
|
+
updatedFiles.push({
|
|
182
|
+
file: path.basename(jsonFile),
|
|
183
|
+
updates: updates
|
|
184
|
+
});
|
|
117
185
|
}
|
|
118
186
|
} catch (error) {
|
|
119
|
-
// Continue with next file
|
|
187
|
+
// Continue with next file on error
|
|
120
188
|
}
|
|
121
189
|
}
|
|
122
190
|
|
|
123
191
|
return {
|
|
124
|
-
success:
|
|
125
|
-
|
|
126
|
-
|
|
192
|
+
success: updatedJsonCount > 0,
|
|
193
|
+
message: updatedJsonCount > 0 ? 'Updated' : 'No references to update',
|
|
194
|
+
updatedJsonCount,
|
|
195
|
+
totalUpdates,
|
|
196
|
+
jsonFiles: updatedFiles
|
|
127
197
|
};
|
|
128
198
|
}
|
|
129
199
|
|
|
130
200
|
/**
|
|
131
|
-
*
|
|
201
|
+
* Finds changed CSX files in Git
|
|
202
|
+
* @param {string} projectRoot - Project root folder
|
|
203
|
+
* @returns {Promise<string[]>} Changed CSX file paths
|
|
132
204
|
*/
|
|
133
205
|
async function getGitChangedCsx(projectRoot) {
|
|
134
206
|
const { exec } = require('child_process');
|
|
135
207
|
const util = require('util');
|
|
136
208
|
const execPromise = util.promisify(exec);
|
|
209
|
+
const fsSync = require('fs');
|
|
137
210
|
|
|
138
211
|
try {
|
|
139
|
-
//
|
|
212
|
+
// Find git root
|
|
140
213
|
const { stdout: gitRoot } = await execPromise('git rev-parse --show-toplevel', { cwd: projectRoot });
|
|
141
214
|
const gitRootDir = gitRoot.trim();
|
|
142
215
|
|
|
143
|
-
//
|
|
216
|
+
// Run git status from git root
|
|
144
217
|
const { stdout } = await execPromise('git status --porcelain', { cwd: gitRootDir });
|
|
145
218
|
const lines = stdout.split('\n').filter(Boolean);
|
|
146
219
|
|
|
@@ -158,7 +231,7 @@ async function getGitChangedCsx(projectRoot) {
|
|
|
158
231
|
.filter(file => {
|
|
159
232
|
// Only .csx files that exist and are in our project
|
|
160
233
|
return file.endsWith('.csx') &&
|
|
161
|
-
|
|
234
|
+
fsSync.existsSync(file) &&
|
|
162
235
|
file.startsWith(path.normalize(projectRoot));
|
|
163
236
|
});
|
|
164
237
|
|
|
@@ -169,18 +242,19 @@ async function getGitChangedCsx(projectRoot) {
|
|
|
169
242
|
}
|
|
170
243
|
|
|
171
244
|
/**
|
|
172
|
-
*
|
|
245
|
+
* Finds all CSX files in discovered components ONLY
|
|
246
|
+
* Does NOT scan folders outside of paths definition
|
|
247
|
+
* @param {string} projectRoot - Project root folder
|
|
248
|
+
* @returns {Promise<string[]>} CSX file paths
|
|
173
249
|
*/
|
|
174
250
|
async function findAllCsx(projectRoot) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
ignore: ['**/node_modules/**', '**/dist/**']
|
|
178
|
-
});
|
|
179
|
-
return files;
|
|
251
|
+
const { findAllCsxInComponents } = require('./discover');
|
|
252
|
+
return findAllCsxInComponents(projectRoot);
|
|
180
253
|
}
|
|
181
254
|
|
|
182
255
|
module.exports = {
|
|
183
256
|
encodeToBase64,
|
|
257
|
+
readNativeContent,
|
|
184
258
|
findJsonFilesForCsx,
|
|
185
259
|
getCsxLocation,
|
|
186
260
|
updateCodeInJson,
|
|
@@ -188,4 +262,3 @@ module.exports = {
|
|
|
188
262
|
getGitChangedCsx,
|
|
189
263
|
findAllCsx
|
|
190
264
|
};
|
|
191
|
-
|
package/src/lib/discover.js
CHANGED
|
@@ -1,65 +1,167 @@
|
|
|
1
1
|
const { glob } = require('glob');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
const COMPONENTS = ['Workflows', 'Tasks', 'Schemas', 'Views', 'Functions', 'Extensions'];
|
|
4
|
+
const { getComponentsRoot, getComponentTypes } = require('./vnextConfig');
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
7
|
+
* Discovers component folders based on vnext.config.json paths
|
|
8
|
+
* Only scans folders defined in paths, ignores everything else
|
|
9
|
+
* @param {string} projectRoot - Project root folder (PROJECT_ROOT)
|
|
10
|
+
* @returns {Object} Discovered component folders
|
|
9
11
|
*/
|
|
10
12
|
async function discoverComponents(projectRoot) {
|
|
13
|
+
const componentsRoot = getComponentsRoot(projectRoot);
|
|
14
|
+
const componentTypes = getComponentTypes(projectRoot);
|
|
15
|
+
|
|
11
16
|
const discovered = {};
|
|
12
17
|
|
|
13
|
-
for
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// Only look for folders defined in paths
|
|
19
|
+
for (const [type, folderName] of Object.entries(componentTypes)) {
|
|
20
|
+
const componentDir = path.join(componentsRoot, folderName);
|
|
21
|
+
|
|
22
|
+
if (fs.existsSync(componentDir) && fs.statSync(componentDir).isDirectory()) {
|
|
23
|
+
discovered[type] = componentDir;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return discovered;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Finds all JSON files in a specific component folder
|
|
32
|
+
* Scans subfolders recursively
|
|
33
|
+
* Ignores .meta folders and *.diagram.json files
|
|
34
|
+
* @param {string} componentDir - Component folder
|
|
35
|
+
* @returns {Promise<string[]>} JSON file paths
|
|
36
|
+
*/
|
|
37
|
+
async function findJsonInComponent(componentDir) {
|
|
38
|
+
const pattern = path.join(componentDir, '**/*.json');
|
|
39
|
+
|
|
40
|
+
const files = await glob(pattern, {
|
|
41
|
+
ignore: [
|
|
42
|
+
'**/.meta/**',
|
|
43
|
+
'**/.meta',
|
|
44
|
+
'**/*.diagram.json',
|
|
45
|
+
'**/package*.json',
|
|
46
|
+
'**/*config*.json'
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Finds all JSON files in discovered components ONLY
|
|
55
|
+
* Does NOT scan folders outside of paths definition
|
|
56
|
+
* @param {Object} discovered - Discovered component folders
|
|
57
|
+
* @returns {Promise<Object[]>} JSON file info (path, type, fileName)
|
|
58
|
+
*/
|
|
59
|
+
async function findAllJsonFiles(discovered) {
|
|
60
|
+
const allFiles = [];
|
|
61
|
+
|
|
62
|
+
// Only scan folders that were discovered from paths
|
|
63
|
+
for (const [type, componentDir] of Object.entries(discovered)) {
|
|
64
|
+
if (componentDir) {
|
|
65
|
+
const files = await findJsonInComponent(componentDir);
|
|
21
66
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
allFiles.push({
|
|
69
|
+
path: file,
|
|
70
|
+
type: type,
|
|
71
|
+
fileName: path.basename(file)
|
|
72
|
+
});
|
|
28
73
|
}
|
|
29
|
-
} catch (error) {
|
|
30
|
-
// Ignore errors
|
|
31
74
|
}
|
|
32
75
|
}
|
|
33
76
|
|
|
34
|
-
return
|
|
77
|
+
return allFiles;
|
|
35
78
|
}
|
|
36
79
|
|
|
37
80
|
/**
|
|
38
|
-
*
|
|
81
|
+
* Finds all CSX files in discovered components ONLY
|
|
82
|
+
* Does NOT scan folders outside of paths definition
|
|
83
|
+
* @param {string} projectRoot - Project root folder
|
|
84
|
+
* @returns {Promise<string[]>} CSX file paths
|
|
85
|
+
*/
|
|
86
|
+
async function findAllCsxInComponents(projectRoot) {
|
|
87
|
+
const discovered = await discoverComponents(projectRoot);
|
|
88
|
+
const allCsxFiles = [];
|
|
89
|
+
|
|
90
|
+
// Only scan folders that were discovered from paths
|
|
91
|
+
for (const [type, componentDir] of Object.entries(discovered)) {
|
|
92
|
+
if (componentDir) {
|
|
93
|
+
const pattern = path.join(componentDir, '**/*.csx');
|
|
94
|
+
const files = await glob(pattern, {
|
|
95
|
+
ignore: [
|
|
96
|
+
'**/.meta/**',
|
|
97
|
+
'**/.meta',
|
|
98
|
+
'**/node_modules/**',
|
|
99
|
+
'**/dist/**'
|
|
100
|
+
]
|
|
101
|
+
});
|
|
102
|
+
allCsxFiles.push(...files);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return allCsxFiles;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Gets a specific component folder
|
|
111
|
+
* @param {Object} discovered - Discovered component folders
|
|
112
|
+
* @param {string} component - Component type
|
|
113
|
+
* @returns {string|null} Folder path
|
|
39
114
|
*/
|
|
40
115
|
function getComponentDir(discovered, component) {
|
|
41
116
|
return discovered[component] || null;
|
|
42
117
|
}
|
|
43
118
|
|
|
44
119
|
/**
|
|
45
|
-
*
|
|
120
|
+
* Lists discovered folders
|
|
121
|
+
* @param {Object} discovered - Discovered component folders
|
|
122
|
+
* @param {Object} componentTypes - All component types from paths
|
|
123
|
+
* @returns {Object[]} Folder list
|
|
46
124
|
*/
|
|
47
|
-
function listDiscovered(discovered) {
|
|
125
|
+
function listDiscovered(discovered, componentTypes) {
|
|
48
126
|
const results = [];
|
|
49
|
-
|
|
127
|
+
|
|
128
|
+
for (const [type, folderName] of Object.entries(componentTypes)) {
|
|
50
129
|
results.push({
|
|
51
|
-
name:
|
|
52
|
-
|
|
53
|
-
|
|
130
|
+
name: type,
|
|
131
|
+
folderName: folderName,
|
|
132
|
+
path: discovered[type] || null,
|
|
133
|
+
found: !!discovered[type]
|
|
54
134
|
});
|
|
55
135
|
}
|
|
136
|
+
|
|
56
137
|
return results;
|
|
57
138
|
}
|
|
58
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Detects component type from file path
|
|
142
|
+
* @param {string} filePath - File path
|
|
143
|
+
* @param {Object} componentTypes - Component type -> folder name mapping
|
|
144
|
+
* @returns {string} Component type
|
|
145
|
+
*/
|
|
146
|
+
function detectComponentTypeFromPath(filePath, componentTypes) {
|
|
147
|
+
const normalizedPath = filePath.toLowerCase();
|
|
148
|
+
|
|
149
|
+
for (const [type, folderName] of Object.entries(componentTypes)) {
|
|
150
|
+
const folderPattern = `/${folderName.toLowerCase()}/`;
|
|
151
|
+
if (normalizedPath.includes(folderPattern)) {
|
|
152
|
+
return type;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return 'unknown';
|
|
157
|
+
}
|
|
158
|
+
|
|
59
159
|
module.exports = {
|
|
60
160
|
discoverComponents,
|
|
161
|
+
findJsonInComponent,
|
|
162
|
+
findAllJsonFiles,
|
|
163
|
+
findAllCsxInComponents,
|
|
61
164
|
getComponentDir,
|
|
62
165
|
listDiscovered,
|
|
63
|
-
|
|
166
|
+
detectComponentTypeFromPath
|
|
64
167
|
};
|
|
65
|
-
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
let cachedConfig = null;
|
|
5
|
+
let cachedProjectRoot = null;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Reads and parses vnext.config.json file
|
|
9
|
+
* @param {string} projectRoot - Project root folder
|
|
10
|
+
* @returns {Object} vnext.config.json content
|
|
11
|
+
*/
|
|
12
|
+
function loadVnextConfig(projectRoot) {
|
|
13
|
+
// Cache check
|
|
14
|
+
if (cachedConfig && cachedProjectRoot === projectRoot) {
|
|
15
|
+
return cachedConfig;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const configPath = path.join(projectRoot, 'vnext.config.json');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(configPath)) {
|
|
21
|
+
throw new Error(`vnext.config.json not found: ${configPath}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
26
|
+
cachedConfig = JSON.parse(content);
|
|
27
|
+
cachedProjectRoot = projectRoot;
|
|
28
|
+
return cachedConfig;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new Error(`Failed to read vnext.config.json: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns domain information
|
|
36
|
+
* @param {string} projectRoot - Project root folder
|
|
37
|
+
* @returns {string} Domain name
|
|
38
|
+
*/
|
|
39
|
+
function getDomain(projectRoot) {
|
|
40
|
+
const config = loadVnextConfig(projectRoot);
|
|
41
|
+
|
|
42
|
+
if (!config.domain) {
|
|
43
|
+
throw new Error('domain not found in vnext.config.json');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return config.domain;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns paths information
|
|
51
|
+
* @param {string} projectRoot - Project root folder
|
|
52
|
+
* @returns {Object} Paths object
|
|
53
|
+
*/
|
|
54
|
+
function getPaths(projectRoot) {
|
|
55
|
+
const config = loadVnextConfig(projectRoot);
|
|
56
|
+
|
|
57
|
+
if (!config.paths) {
|
|
58
|
+
throw new Error('paths not found in vnext.config.json');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return config.paths;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns components root folder
|
|
66
|
+
* @param {string} projectRoot - Project root folder
|
|
67
|
+
* @returns {string} Components root folder path
|
|
68
|
+
*/
|
|
69
|
+
function getComponentsRoot(projectRoot) {
|
|
70
|
+
const paths = getPaths(projectRoot);
|
|
71
|
+
|
|
72
|
+
if (!paths.componentsRoot) {
|
|
73
|
+
throw new Error('paths.componentsRoot not found in vnext.config.json');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return path.join(projectRoot, paths.componentsRoot);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns component types and folder names
|
|
81
|
+
* @param {string} projectRoot - Project root folder
|
|
82
|
+
* @returns {Object} Component type -> folder name mapping
|
|
83
|
+
*/
|
|
84
|
+
function getComponentTypes(projectRoot) {
|
|
85
|
+
const paths = getPaths(projectRoot);
|
|
86
|
+
|
|
87
|
+
// All paths except componentsRoot are component types
|
|
88
|
+
const componentTypes = {};
|
|
89
|
+
|
|
90
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
91
|
+
if (key !== 'componentsRoot') {
|
|
92
|
+
componentTypes[key] = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return componentTypes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns full configuration
|
|
101
|
+
* @param {string} projectRoot - Project root folder
|
|
102
|
+
* @returns {Object} Full vnext.config.json content
|
|
103
|
+
*/
|
|
104
|
+
function getFullConfig(projectRoot) {
|
|
105
|
+
return loadVnextConfig(projectRoot);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Clears the cache
|
|
110
|
+
*/
|
|
111
|
+
function clearCache() {
|
|
112
|
+
cachedConfig = null;
|
|
113
|
+
cachedProjectRoot = null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
loadVnextConfig,
|
|
118
|
+
getDomain,
|
|
119
|
+
getPaths,
|
|
120
|
+
getComponentsRoot,
|
|
121
|
+
getComponentTypes,
|
|
122
|
+
getFullConfig,
|
|
123
|
+
clearCache
|
|
124
|
+
};
|